How to serialize PKCS10CertificationRequest in BouncyCastle to send it over network? - java

I have been trying to serialize the object PKCS10CertificationRequest for a while now. I figured the right way to do it is to create an ASN1Primitive class, send it over the network, then deserialize it. However, there seems to be only serialization into ASN1, but there seems to be no deserialization from ASN1, and I don't want to manually parse and reconstruct the Request. What should I do? My code so far is
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "SC");
kpg.initialize(1024);
KeyPair kp = kpg.genKeyPair();
System.out.println("Private: " + kp.getPrivate());
System.out.println("Public: " + kp.getPublic());
X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE);
x500NameBld.addRDN(BCStyle.C, "AU");
x500NameBld.addRDN(BCStyle.O, "The Legion of the Bouncy Castle");
x500NameBld.addRDN(BCStyle.L, "Melbourne");
x500NameBld.addRDN(BCStyle.ST, "Victoria");
x500NameBld.addRDN(BCStyle.EmailAddress, "feedback-crypto#bouncycastle.org");
X500Name subject = x500NameBld.build();
PKCS10CertificationRequestBuilder requestBuilder = new JcaPKCS10CertificationRequestBuilder(subject, kp.getPublic());
PKCS10CertificationRequest req1 = requestBuilder.build(new JcaContentSignerBuilder("SHA1withRSA").setProvider("SC").build(
kp.getPrivate()));
JcaPKCS10CertificationRequest req2 = new JcaPKCS10CertificationRequest(req1.getEncoded()).setProvider("SC");
//serialization
ByteArrayOutputStream abOut = new ByteArrayOutputStream();
ASN1OutputStream berOut = new ASN1OutputStream(abOut);
berOut.writeObject(req2.toASN1Structure());
byte[] serializedData = abOut.toByteArray();
ASN1Primitive asn1Primitive = ASN1Primitive.fromByteArray(serializedData);
System.out.println("");
System.out.println("" + asn1Primitive.toString());
And the output is
[[0, [[[2.5.4.6, AU]], [[2.5.4.10, The Legion of the Bouncy Castle]], [[2.5.4.7, Melbourne]], [[2.5.4.8, Victoria]], [[1.2.840.113549.1.9.1, feedback-crypto#bouncycastle.org]]], [[1.2.840.113549.1.1.1, NULL], #03818D0030818902818100A...
I don't want to parse this manually. What should I do instead?

Forget about ASN1, it is a mess, and there seems to be no automatic deserialization. However, you can use the JcaPEMWriter and PEMParser classes in BouncyCastle to create a String object to serialize or deserialize the data, and send it over the network.
StringWriter sw = new StringWriter();
JcaPEMWriter pemWriter = new JcaPEMWriter(sw);
pemWriter.writeObject(req2);
pemWriter.close();
PEMParser pemParser = null;
try
{
pemParser = new PEMParser(new StringReader(sw.toString()));
Object parsedObj = pemParser.readObject();
System.out.println("PemParser returned: " + parsedObj);
if (parsedObj instanceof PKCS10CertificationRequest)
{
JcaPKCS10CertificationRequest jcaPKCS10CertificationRequest = new JcaPKCS10CertificationRequest((PKCS10CertificationRequest)parsedObj);
System.out.println("" + jcaPKCS10CertificationRequest.getPublicKey());
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
finally
{
if (pemParser != null)
{
pemParser.close();
}
}
EDIT: Although if someone really needs to get elements out of ASN1Encodable object (like an RDN of X500Name, apparently you need the IETFUtils class as per https://stackoverflow.com/a/5527171/2413303 .

Related

How to encrypt Credentials object - Cipher.doFinal, SealedObject, or CipherOutputStream?

I need to encrypt a set of user credentials and send it to a SOAP web service. The following code snippet (I think it's C#) is provided in the documentation, and my Java code is based on it.
private string Encrypt256(string text, AesCryptoServiceProvider aes)
{
// Convert string to byte array
byte[] src = Encoding.Unicode.GetBytes(text);
// encryption
using (ICryptoTransform encrypt = aes.CreateEncryptor())
{
byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);
// Convert byte array to Base64 strings
return Convert.ToBase64String(dest);
}
}
...
Credentials credential = new Credentials();
credential.UserName = "username";
credential.Password = "password";
credential.ClientUtcTime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", System.Globalization.CultureInfo.InvariantCulture);
//--Serialize credential
XmlSerializer serializer = new XmlSerializer(credential.GetType());
string xmlCredential = string.Empty;
using (var stringwriter = new System.IO.StringWriter())
{
serializer.Serialize(stringwriter, credential);
xmlCredential = stringwriter.ToString();
}
//--Encrypt credential with AES256 symmetric
String encryptedCredential = Encrypt256(xmlCredential, aesServiceProvider);
...
The following is my Java code.
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256);
SecretKey sk = kg.generateKey();
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE, sk);
Credentials cred = new UsernamePasswordCredentials("username", "password");//no need for time field?
String eCred = Base64.encodeBase64String(aesCipher.doFinal(objectToByteArray(cred)));
...
private byte[] objectToByteArray(Object obj) {
byte[] bytes = null;
try (
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
oos.writeObject(obj);
oos.flush();
bytes = bos.toByteArray();
}
catch (IOException e) {
e.printStackTrace();
}
return bytes;
}
Then I came across SealedObject and CipherOutputStream. I tried writing code snippets for those.
Using SealedObject
// slight change here; cred must implement Serializable
UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username", "password");
// same as above except for the following two lines
SealedObject so = new SealedObject(cred, aesCipher);
String eCred = Base64.encodeBase64String(objectToByteArray(so));
Using CipherOutputStream
Credentials cred = new UsernamePasswordCredentials("username", "password");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
CipherOutputStream cos = new CipherOutputStream(bos, aesCipher);
cos.write(objectToByteArray(cred));
cos.close();
String eCred = Base64.encodeBase64String(bos.toByteArray());
For all three code snippets, is the code correct? Considering that this code will be called frequently, which approach is the most efficient?

Create JWT (Json Web Token) with RSA encryption using Java library

I am looking to develop a JWT app with RSA encryption using "Nimbus JOSE+JWT" library. I am seeking sample code.
I would like to use the following Maven dependency:
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>3.10</version>
</dependency>
Note: Please always use the latest version from Maven Central repository.
If you're using the latest version of 4.23 of 'Nimbus Jose JWT' then there is some minor changes in the API.
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>4.23</version>
</dependency>
I've shown the code below for reference:
public class JwtJoseExample {
public static void main(String[] args) {
KeyPairGenerator keyPairGenerator;
try {
keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(1024);
// generate the key pair
KeyPair keyPair = keyPairGenerator.genKeyPair();
// create KeyFactory and RSA Keys Specs
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(keyPair.getPublic(), RSAPublicKeySpec.class);
RSAPrivateKeySpec privateKeySpec = keyFactory.getKeySpec(keyPair.getPrivate(), RSAPrivateKeySpec.class);
// generate (and retrieve) RSA Keys from the KeyFactory using Keys Specs
RSAPublicKey publicRsaKey = (RSAPublicKey) keyFactory.generatePublic(publicKeySpec);
RSAPrivateKey privateRsaKey = (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder();
claimsSet.issuer("https://my-auth-server.com");
claimsSet.subject("John Kerr");
claimsSet.audience(getAudience());
claimsSet.expirationTime(new Date(new Date().getTime() + 1000*60*10));
claimsSet.notBeforeTime(new Date());
claimsSet.jwtID(UUID.randomUUID().toString());
System.out.println("--------------------------");
System.out.println("Claim Set : \n"+claimsSet.build());
// create the JWT header and specify:
// RSA-OAEP as the encryption algorithm
// 128-bit AES/GCM as the encryption method
JWEHeader header = new JWEHeader(JWEAlgorithm.RSA_OAEP, EncryptionMethod.A128GCM);
// create the EncryptedJWT object
EncryptedJWT jwt = new EncryptedJWT(header, claimsSet.build());
// create an RSA encrypter with the specified public RSA key
RSAEncrypter encrypter = new RSAEncrypter(publicRsaKey);
// do the actual encryption
jwt.encrypt(encrypter);
// serialize to JWT compact form
String jwtString = jwt.serialize();
System.out.println("\nJwt Compact Form : "+jwtString);
// in order to read back the data from the token using your private RSA key:
// parse the JWT text string using EncryptedJWT object
jwt = EncryptedJWT.parse(jwtString);
// create a decrypter with the specified private RSA key
RSADecrypter decrypter = new RSADecrypter(privateRsaKey);
// do the decryption
jwt.decrypt(decrypter);
// print out the claims
System.out.println("===========================================================");
System.out.println("Issuer: [ " + jwt.getJWTClaimsSet().getIssuer() + "]");
System.out.println("Subject: [" + jwt.getJWTClaimsSet().getSubject()+ "]");
System.out.println("Audience size: [" + jwt.getJWTClaimsSet().getAudience().size()+ "]");
System.out.println("Expiration Time: [" + jwt.getJWTClaimsSet().getExpirationTime()+ "]");
System.out.println("Not Before Time: [" + jwt.getJWTClaimsSet().getNotBeforeTime()+ "]");
System.out.println("Issue At: [" + jwt.getJWTClaimsSet().getIssueTime()+ "]");
System.out.println("JWT ID: [" + jwt.getJWTClaimsSet().getJWTID()+ "]");
System.out.println("===========================================================");
} catch (NoSuchAlgorithmException | InvalidKeySpecException | JOSEException | ParseException e) {
System.out.println(e.getMessage());
}
}
private static List<String> getAudience(){
List<String> audience = new ArrayList<>();
audience.add("https://my-web-app.com");
audience.add("https://your-web-app.com");
return audience;
}
}
The output is:
Claim Set :
{"sub":"John Kerr","aud":["https:\/\/my-web-app.com","https:\/\/your-web-app.com"],"nbf":1471116052,"iss":"https:\/\/my-auth-server.com","exp":1471116652,"jti":"8769fc6d-b69f-45e3-b1a5-52695c23675e"}
Jwt Compact Form :
eyJlbmMiOiJBMTI4R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.f2ZZyMaJi03RqunyWsLvIr7tNX1KvTRUcpN9qsBHvbXIouZCyepQMsbI1v88GHuLIV3f6SviCDyICp7kZCj_9q-yIi_dIuroADWQ-P_UnqNPRXQ1yEwbmrLUK80lBtKyc3Z6g_3Db_HLtD6QPEq-zUAh3wJ7uSPxhql2oc9otGc.Xbyrf4iWM0shNp4S.TCKoJGAEQ4rpJJk2qZP11awxTEWTg-r5VpppGgZNhiHCBhnGnyR2sb86O7ISc3j-i4OYp7Xl2vThzztD1ojy_IKPYQkg_iACuo6yjzdopQQT143vjYuFLfFhQFfjfCoO6iibTqK7vmqfF0bUWD6Nj-4MwjlW6dFV7mNQEN50dkiMrFMZkmiVKZRa50jOK1NcoWYiKSrCOQduJYibJfF6jSQARX9MsX5tib-3BXSsKtPLNrQ6mFfvDzzruBuO4gKWLE3PQPUfIQ.gXt5KcpxEbfYLP854GvcQw
===========================================================
Issuer: [ https://my-auth-server.com]
Subject: [John Kerr]
Audience size: [2]
Expiration Time: [Sun Aug 14 01:00:52 IST 2016]
Not Before Time: [Sun Aug 14 00:50:52 IST 2016]
Issue At: [null]
JWT ID: [8769fc6d-b69f-45e3-b1a5-52695c23675e]
===========================================================
//This is a complete encryption and decryption module using
//Algorithm: JWEAlgorithm.RSA_OAEP_256
//Encryption Method: A128CBC_HS256
public static String encrypt(String text) throws Exception {
// Set the plain text
Payload payload = new Payload(text);
// Create the header
JWEHeader header = new JWEHeader(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A128CBC_HS256);
// Create the JWE object and encrypt it
JWEObject jweObject = new JWEObject(header, payload);
jweObject.encrypt(new RSAEncrypter(getPublicKey()));
// Serialise to compact JOSE form...
String jweString = jweObject.serialize();
LOG.info("Generated Encrypted Key : {}", jweString);
return jweString;
}
public static String decrypt(String text) throws Exception {
// Parse into JWE object...
JWEObject jweObject = JWEObject.parse(text);
jweObject.decrypt(new RSADecrypter(getPrivateKey()));
// Get the plain text
Payload payload = jweObject.getPayload();
System.out.println(payload.toString());
return payload.toString();
}
private static RSAPublicKey getPublicKey() throws Exception {
String filename = "/home/vaibhav/Setups/cert/pub.der";
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return (RSAPublicKey) kf.generatePublic(spec);
}
private static RSAPrivateKey getPrivateKey() throws Exception {
String filename = "/home/vaibhav/Setups/cert/private.pkcs8";
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)f.length()];
dis.readFully(keyBytes);
dis.close();
PKCS8EncodedKeySpec spec1 = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) kf.generatePrivate(spec1);
}
I implemented the code to use "nimbus-jose-jwt" library, please find the code below
JWTClaimsSet jwtClaims = new JWTClaimsSet();
jwtClaims.setIssuer(ISSUER);
jwtClaims.setSubject(SUBJECT);
List<String> aud = new ArrayList<String>();
aud.add("https://app-one.com");
aud.add("https://app-two.com");
jwtClaims.setAudience(aud);
// Set expiration in 10 minutes
jwtClaims.setExpirationTime(new Date(new Date().getTime() + 1000*60*10));
jwtClaims.setNotBeforeTime(new Date());
jwtClaims.setIssueTime(new Date());
jwtClaims.setJWTID(UUID.randomUUID().toString());
System.out.println("=========== JWT CLAMINS =============");
System.out.println(jwtClaims.toJSONObject());
// Request JWT encrypted with RSA-OAEP and 128-bit AES/GCM
JWEHeader header = new JWEHeader(JWEAlgorithm.RSA_OAEP, EncryptionMethod.A128GCM);
// Create the encrypted JWT object
EncryptedJWT jwt = new EncryptedJWT(header, jwtClaims);
// Create an encrypter with the specified public RSA key
RSAEncrypter encrypter = new RSAEncrypter(publicKey);
jwt.encrypt(encrypter);
String jwtString = jwt.serialize();
System.out.println(jwtString);
// Parse back
jwt = EncryptedJWT.parse(jwtString);
// Create a decrypter with the specified private RSA key
RSADecrypter decrypter = new RSADecrypter(privateKey);
// Decrypt
jwt.decrypt(decrypter);
// Retrieve JWT claims
System.out.println(jwt.getJWTClaimsSet().getIssuer());;
System.out.println(jwt.getJWTClaimsSet().getSubject());
System.out.println(jwt.getJWTClaimsSet().getAudience().size());
System.out.println(jwt.getJWTClaimsSet().getExpirationTime());
System.out.println(jwt.getJWTClaimsSet().getNotBeforeTime());
System.out.println(jwt.getJWTClaimsSet().getIssueTime());
System.out.println(jwt.getJWTClaimsSet().getJWTID());
This code has also use the public and private keys to create JWT token, also you will need those keys to extract claims and validate it.
Once you run this code, you should be able to see following jwtClaims:
{
"exp": 1429126207,
"sub": "alice",
"nbf": 1429125607,
"aud": [
"https://app-one.com",
"https://app-two.com"
],
"iss": "A FaceBook Cooperation",
"jti": "5c94d2db-e818-4746-b2f2-a4cd084d5a44",
"iat": 1429125607
}

How do you print a PKCS10CertificationRequest as a String?

Is there a way to print the CSR generated with PKCS10CertificationRequest class? I am struggling to see the generated request.
PKCS10CertificationRequest certRequest = new PKCS10CertificationRequest(fromByteArray);
System.out.println("CSR string = "+certRequest.toString());
System.out.println("CSR Subject Name = "+certRequest.getSubject().toString());
System.out.println("CSR Subject PubkeyInfo = "+certRequest.getSubjectPublicKeyInfo().toString());
Hope this can help:
PemObject pemObject = new PemObject("CERTIFICATE REQUEST", certRequest.getEncoded());
StringWriter str = new StringWriter();
PEMWriter pemWriter = new PEMWriter(str);
pemWriter.writeObject(pemObject);
pemWriter.close();
str.close();
System.out.println(str);

Get KeyPair from PEM Key with BouncyCastle

I have a PEM Key and I want to get a KeyPair with it and bouncycastle. I found this code which seems good but I have a cast exception.
function loadKey() {
File privateKeyFile = new File(keyPath);
PEMParser pemParser = new PEMParser(new FileReader(privateKeyFile));
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
Object object = pemParser.readObject();
KeyPair kp;
if (object instanceof PEMEncryptedKeyPair) {
Logger.info("Encrypted key - we will use provided password");
kp = converter.getKeyPair(((PEMEncryptedKeyPair) object).decryptKeyPair(decProv));
}
else {
Logger.info("Unencrypted key - no password needed");
kp = converter.getKeyPair((PEMKeyPair) object);
}
return kp;
}
And it returns me :
Unencrypted key - no password needed
org.bouncycastle.asn1.x509.SubjectPublicKeyInfo cannot be cast to org.bouncycastle.openssl.PEMKeyPair
I tried several methods but i didn't succeed.
Thanks to help me :)
If you have a private key that has passphrase you might get this exception. Try removing the passphrase:
openssl rsa -in /path/to/originalkeywithpass.key -out /path/to/newkeywithnopass.key
You can use below code
PEMKeyPair pemKeyPair = (PEMKeyPair) pp.readObject();
KeyPair kp = new JcaPEMKeyConverter().getKeyPair(pemKeyPair);
pp.close();
For example:
public PrivateKey getKeyFromClassPath(String filename) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream stream = loader.getResourceAsStream("certificates/" + filename);
if (stream == null) {
throw new CertificateException("Could not read private key from classpath:" + "certificates/" + filename);
}
BufferedReader br = new BufferedReader(new InputStreamReader(stream));
try {
Security.addProvider(new BouncyCastleProvider());
PEMParser pp = new PEMParser(br);
PEMKeyPair pemKeyPair = (PEMKeyPair) pp.readObject();
KeyPair kp = new JcaPEMKeyConverter().getKeyPair(pemKeyPair);
pp.close();
return kp.getPrivate();
} catch (IOException ex) {
throw new CertificateException("Could not read private key from classpath", ex);
}
}

Generate passbook signature in Java

I haven't seen any examples on the internet for this so as far as I know this is the first time someone is trying this in Java which I find hard to believe.
I'm just trying to work with the .pem, .p12 and .cer files I've been given to generate a signature file for my manifest.json. Here is what I have, which gives me an InvalidKeyException version mismatch: (supported: 00, parsed: 03
See the comment in the code below where the error is happening. I've viewed a few examples in another languages of how people are doing this with openssl but there must be a Java equivalent??
File pemFile = new File("AWWdevCert.pem");
File passCer = new File("pass.cer");
File passP12 = new File("pass.p12");
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
KeySpec ks = new PKCS8EncodedKeySpec(FileUtils.readFileToByteArray(passP12));
PrivateKey privKey = keyFactory.generatePrivate(ks); // ERROR HERE
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
InputStream in = new ByteArrayInputStream(FileUtils.readFileToByteArray(passCer));
X509Certificate passCert = (X509Certificate)certFactory.generateCertificate(in); //don't know what to do with this
File inputFile = new File("WebContent/WEB-INF/Lowes.raw/manifest.json");
FileInputStream freader = null;
int sizecontent = ((int) inputFile.length());
byte[] contentbytes = new byte[sizecontent];
freader = new FileInputStream(inputFile);
System.out.println("\nContent Bytes: " + freader.read(contentbytes, 0, sizecontent));
freader.close();
Signature signature = Signature.getInstance("Sha1WithRSA");
signature.initSign(privKey);
signature.update(contentbytes);
byte[] signedData = signature.sign();
//create signature file
File signatureFile = new File(passDirectory.getAbsolutePath()+File.separator+"signature");
Check this jpasskit project on github
You can also generate signature by using only native sun.security package. Here is an example in Scala (can be easily rewritten for Java)
import java.security.cert.X509Certificate
import java.security.{MessageDigest, PrivateKey, Signature}
import java.util.Date
import sun.security.pkcs._
import sun.security.util.DerOutputStream
import sun.security.x509.{AlgorithmId, X500Name}
object PKPassSigner {
def sign(
signingCert: X509Certificate,
privateKey: PrivateKey,
intermediateCert: X509Certificate,
dataToSing: Array[Byte]
): Array[Byte] = {
val digestAlgorithmId = new AlgorithmId(AlgorithmId.SHA_oid)
val md = MessageDigest.getInstance(digestAlgorithmId.getName)
val attributes = new PKCS9Attributes(Array(
new PKCS9Attribute(PKCS9Attribute.SIGNING_TIME_OID, new Date()),
new PKCS9Attribute(PKCS9Attribute.MESSAGE_DIGEST_OID, md.digest(dataToSign)),
new PKCS9Attribute(PKCS9Attribute.CONTENT_TYPE_OID, ContentInfo.DATA_OID)
))
val signature = Signature.getInstance("Sha1WithRSA")
signature.initSign(privateKey)
signature.update(attributes.getDerEncoding)
val signedData = signature.sign()
val signerInfo = new SignerInfo(
X500Name.asX500Name(signingCert.getIssuerX500Principal),
signingCert.getSerialNumber,
digestAlgorithmId,
attributes,
AlgorithmId.get(privateKey.getAlgorithm),
signedData,
null
)
val p7 = new PKCS7(
Array(digestAlgorithmId),
new ContentInfo(ContentInfo.DATA_OID, null),
Array(signingCert, intermediateCert),
Array(signerInfo)
)
val out = new DerOutputStream()
p7.encodeSignedData(out)
out.flush()
val res = out.toByteArray
out.close()
res
}
}

Categories