sign file with bouncy castle in java - java

I want to sign a file content with certificate in java.
With terminal and openssl, I can do this :
openssl smime -sign -in nosign.mobileconfig -out signed.mobileconfig -signer server.crt -inkey server.key -certfile cacert.crt -outform der -nodetach
server.crt and .key are the files to sign, and I think I understand the cacert.crt is embedded inside the out content.
finally, I have a file signed and trusted.
In Java, I can't use openssl (don't want to launch openssl command) so, I have to sign it with a lib.
To do that, I use Bouncy Castle (version 1.53)
here is my code :
byte[] profile = ...; // I can have it also in String
// the certificate in -certfile
FileInputStream inputStream = new FileInputStream("src/main/resources/cacert.crt");
byte[] caCertificate = ByteStreams.toByteArray(inputStream);
// the certificate to sign : server.crt, embedded in p12
X509Certificate serverCertificate = (X509Certificate) this.keyStore.getCertificate("1");
// Private key is the server.key
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(this.privateKey);
CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
generator.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(
new JcaDigestCalculatorProviderBuilder().setProvider("BC").build()).build(sha1Signer, serverCertificate));
// the embedded certificate : cacert.crt, but I don't know if it is good to do that this way
X509CertificateHolder holder = new X509CertificateHolder(caCertificate);
generator.addCertificate(holder);
CMSProcessableByteArray bytes = new CMSProcessableByteArray(profile);
CMSSignedData signedData = generator.generate(bytes, true);
System.out.println("signedData : \n" + signedData.getEncoded());
Can you help me to have the good signed data please ? Thanks !
EDIT :
I've got an error at
X509CertificateHolder holder = new X509CertificateHolder(caCertificate);
java.io.IOException: unknown tag 13 encountered

The CA certificate file is obviously in PEM (ASCII) format. The constructor for X509CertificateHolder needs the BER/DER (binary) encoding of the certificate.
You can convert it by adding this:
PEMParser pemParser = new PEMParser(new FileReader("src/main/resources/cacert.crt"));
X509CertificateHolder caCertificate = (X509CertificateHolder) pemParser.readObject();
You should add the signing certificate to the CMS structure as well:
generator.addCertificate(new X509CertificateHolder(serverCertificate.getEncoded()));

Related

Problem generating openssl .pk8 file. null value when trying to retrieve parameters

I am trying to generate an RSA private key via openssl. I use the following commands:
openssl genrsa -out rsaprivkey.pem 1024
openssl rsa -in rsaprivkey.pem -pubout -outform DER -out rsapubkey.dem
openssl pkcs8 -topk8 -outform PEM -in rsaprivkey.pem -inform PEM -out private.pk8
openssl req -new -x509 -key rsaprivkey.pem -out certificato.crt -subj "/C=IT/ST=Italia/L=Roma/O=xxxx/OU=xxxx/CN=xxxx"
i get all necessary files. The problem is that when I try to use the .pk8 file in my code, I get a java.lang.NullPointerException when I try to read the parameters that should be contained in the file (AlgorithmParameters params = epki.getAlgParameters();)
//
// The bytes just read are supposed to be in "EncryptedPrivateKeyInfo" info
// The algorithm will have OID 1.2.840.113549.1.5.3 or be called "PBEWithMD5AndDES",
// (actually, according to RFC2898, that would be "pbeWithMD5AndDES-CBC")
// which means "Password Based Encryption Algorithm, uses Data Encryption Standard in
// Cipher Block Chaining Mode (DES-CBC), uses MD5 to hash a password & salt to get Key
// and Initialization Vector. Defined in RSA's PKCS#5". See RFC2898 for more.
//
EncryptedPrivateKeyInfo epki = new EncryptedPrivateKeyInfo(instream);
System.out.println("Encrypted private key info's algorithm name : " + epki.getAlgName());
AlgorithmParameters params = epki.getAlgParameters();
{
PBEParameterSpec pbeParams = (PBEParameterSpec) (params.getParameterSpec(PBEParameterSpec.class));
Hex hex = new Hex();
String salt = new String(hex.encode(pbeParams.getSalt()), "ASCII");
System.out.println("Encrypted private key info's salt : 0x" + salt);
System.out.println("Encrypted private key info's iteration count: " + pbeParams.getIterationCount());
}
//
// The 'keySpec' is transformed into a 'key' (to be used in a cipher) through a SecretKeyFactory
// The password obtained earlier is used to generate a temporary "keySpec" that is used as
// input to the SecretKeyFactory, then scratched again. What about the PBE algorithm parameter?
// We don't need it here (empirically), but we *must* specify it later on in the cipher.
//
Key encryptedKey = null;

Decrypt File in Java Encrypted via openssl generated (S/Mime) key pair

I am trying to decrypt files that were encrypted using an openssl generated public key. I have the key pair that was generated in openssl and can decrypt files with the private key within openssl. However, I am now tasked with decrypting the files in a Java application, using the same private key that was generated via openssl. I have gotten as far as reading the private key into an java.security.PrivateKey object successfully, using Bouncy Castle libraries. I have written (with the help of code I found in various stack overflow posts) three decryption methods, but get various exceptions with each one. I will provide the openssl commands below and also the Java code I used to read in the private key and one of the decrytion methods I've tried.
Generate key pair with openssl:
openssl genrsa -aes256 -out my-private-key.pem 4096
openssl req -new -x509 -days 730 -key my-private-key.pem -out my-public-key.pem
encrypt file
openssl smime -binary -encrypt -in test.file -out test.file.enc my-public-key.pem
decrypt file
openssl smime -binary -decrypt -inkey ecn-private-key.pem -in test.file.enc -out test.file.dec
(Enter pass phrase for my-private-key.pem)
public key header
-----BEGIN CERTIFICATE-----
private key header
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,4EFFB692F0BEBF75B183DB1371979B97
// read private key - this is successful
private static PrivateKey readPrivateKeyPEM(File file, String password) throws IOException,
GeneralSecurityException, OperatorCreationException, PKCSException {
try (FileReader reader = new FileReader(file)) {
PEMParser parser = new PEMParser(reader);
Object object = parser.readObject();
if (object == null) {
throw new IllegalArgumentException("No key found in " + file);
}
BouncyCastleProvider provider = new BouncyCastleProvider();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider(provider);
PEMDecryptorProvider decryptionProvider = new
JcePEMDecryptorProviderBuilder().setProvider(provider).build(password.toCharArray());
PEMKeyPair keypair = ((PEMEncryptedKeyPair) object).decryptKeyPair(decryptionProvider);
return converter.getPrivateKey(keypair.getPrivateKeyInfo());
}
}
// The following method attempts to decrypt file with private key read in above
fails
// this fails
public static void decrypt(PrivateKey privateKey, File encrypted, File decryptedDestination)
throws IOException, CMSException {
byte[] encryptedData = Files.readAllBytes(encrypted.toPath());
CMSEnvelopedDataParser parser = new CMSEnvelopedDataParser(encryptedData);
RecipientInformation recInfo = getSingleRecipient(parser);
Recipient recipient = new JceKeyTransEnvelopedRecipient(privateKey);
try (InputStream decryptedStream = recInfo.getContentStream(recipient).getContentStream()) {
Files.copy(decryptedStream, decryptedDestination.toPath());
}
}
The stack trace is:
Exception in thread "main" org.bouncycastle.cms.CMSException: Unexpected object reading content.
at org.bouncycastle.pkix#1.68/org.bouncycastle.cms.CMSContentInfoParser.(Unknown Source)
at org.bouncycastle.pkix#1.68/org.bouncycastle.cms.CMSEnvelopedDataParser.(Unknown Source)
at org.bouncycastle.pkix#1.68/org.bouncycastle.cms.CMSEnvelopedDataParser.(Unknown Source)
at com.att.floodportal.openssl.EncryptAndDecrypt.decrypt(EncryptAndDecrypt.java:32)
at com.att.floodportal.openssl.JavaSecurityPemUtils.main(JavaSecurityPemUtils.java:77)
Caused by: java.lang.ClassCastException: class org.bouncycastle.asn1.DLApplicationSpecific cannot be cast to class org.bouncycastle.asn1.ASN1SequenceParser (org.bouncycastle.asn1.DLApplicationSpecific and org.bouncycastle.asn1.ASN1SequenceParser are in module org.bouncycastle.provider#1.68 of loader 'app')
Thank You

Outputting encrypted PK8 private key from Java BouncyCastle

I am trying to generate an encrypted private key and CSR using Java in Matlab. Matlab adds some minor complexity, but this is mostly a Java problem. I start with a private key:
java.security.Security.addProvider(org.bouncycastle.jce.provider.BouncyCastleProvider());
keyGen = java.security.KeyPairGenerator.getInstance('RSA', 'BC');
keyGen.initialize(2048, java.security.SecureRandom());
keypair = keyGen.generateKeyPair();
privateKey = keypair.getPrivate();
If I encrypt the key and output it as PEM:
m=org.bouncycastle.openssl.PKCS8Generator.PBE_SHA1_3DES;
encryptorBuilder = org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder(m);
encryptorBuilder.setRandom(java.security.SecureRandom());
encryptorBuilder.setPasssword(password);
oe = encryptorBuilder.build();
gen = org.bouncycastle.openssl.jcajce.JcaPKCS8Generator(privateKey,oe);
privKeyObj = gen.generate();
fos = java.io.FileWriter('private.pem');
pem = org.bouncycastle.openssl.jcajce.JcaPEMWriter(fos);
pem.writeObject(privKeyObj);
pem.flush();
fos.close();
I get a perfectly good key. The problem is that I want to use the key with jdbc, so I need a DER formatted pk8 key. I cannot figure out how to get this out of BouncyCastle. A kludge workaround that succeeds:
textWriter = java.io.StringWriter();
pem = org.bouncycastle.openssl.jcajce.JcaPEMWriter(textWriter);
pem.writeObject(privateKey);
pem.flush();
thekey = char(textWriter.toString());
cmd = ['echo "' thekey '"|openssl pkcs8 -topk8 -out private.pk8 -inform PEM -outform DER -passout pass:' password];
system(cmd);
Now, obviously this exposes both the unencrypted private key and the password. I've tried all manner of things to coerce privKeyObj to DER, but they typically leave me with:
$openssl pkcs8 -inform DER -outform PEM -in private.pk8 -out private.pem
Error decrypting key
140735211835472:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:tasn_dec.c:1201:
140735211835472:error:0D06C03A:asn1 encoding routines:ASN1_D2I_EX_PRIMITIVE:nested asn1 error:tasn_dec.c:765:
140735211835472:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:tasn_dec.c:697:Field=version, Type=PKCS8_PRIV_KEY_INFO
The intent of this code is to generate a CSR on the end user's machine which I then sign, and which is encrypted with MAC address of the machine (and a salt), so that the program will only run on the authorized machine, and only authorized machines will be able to access my PostgreSql database.
Suggestions?
I figured it out. In my original code, I had used BcPKCS12PBEOutputEncryptorBuilder. Wrong! The correct call is to JcePKCSPBEOutputEncryptorBuilder. The correct code (in MATLAB, but converting to Java is simple) is:
java.security.Security.addProvider(org.bouncycastle.jce.provider.BouncyCastleProvider());
keyGen = java.security.KeyPairGenerator.getInstance('RSA', 'BC');
keyGen.initialize(2048, java.security.SecureRandom());
keypair = keyGen.generateKeyPair();
privateKey = keypair.getPrivate();
builder=org.bouncycastle.pkcs.jcajce.JcaPKCS8EncryptedPrivateKeyInfoBuilder(privateKey);
m=org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC;
encryptorBuilder = org.bouncycastle.pkcs.jcajce.JcePKCSPBEOutputEncryptorBuilder(m);
password = 'test';
outputBuilder = encryptorBuilder.build(password);
privKeyObj = builder.build(outputBuilder);
fos = java.io.FileOutputStream('testkey.pk8');
fos.write(privKeyObj.getEncoded());
fos.flush();
fos.close();
This generates a DER formatted PCS#8 file.
openssl pkcs8 -inform DER -outform PEM -in testkey.pk8 -out testkey.pem
Now returns the PEM private key. To read the key:
myPath = java.nio.file.Paths.get(pwd,'testkey.pk8');
encodedKey = java.nio.file.Files.readAllBytes(myPath);
privKeyObj =org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo(encodedKey);
cp=org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter();
cp.setProvider('BC');
decryptorBuilder = org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder();
inputBuilder = decryptorBuilder.build(password);
info = privKeyObj.decryptPrivateKeyInfo(inputBuilder);
decodedKey=cp.getPrivateKey(info);
Note that in MATLAB, you don't need to declare the type of the returned object, and you don't need to put "new" in front of a constructor.

validating through web service and saving password for application

I am developing java application which consumes with the web service, which then validates the user, I have the user enter his username and password. For using this application user required a valid username and password.
I have one context menu which will get activated when there is correct login. Otherwise i want it to get disabled.
And I want only a one time validation. So that, if any other user use that application from same system he dont need to enter the password again.
that means i need to save the password in local system, to use this password throughout the application
Any help regarding saving the password anyhow ?
Well, you can use a public and private key to encrypt or decrypt password.
Edit:
First of all you have to create a public/private key pair. You need the tool openssl for this (http://www.openssl.org/source/ or directly for Windows http://www.openssl.org/related/binaries.html).
Install it, open "cmd" (if you are on windows), navigate to your openssl installation path and enter following lines to generate the keys for server and client:
openssl genrsa -out serverPrivateKey.pem 2048
openssl rsa -in serverPrivateKey.pem -pubout -outform DER -out serverPublicKey.der
openssl genrsa -out clientPrivateKey.pem 2048
openssl pkcs8 -topk8 -nocrypt -in clientPrivateKey.pem -outform der -out clientPrivateKey.der
openssl rsa -in clientPrivateKey.pem -pubout -outform PEM -out clientPublicKey.pem
Now in your web service java application you can import the public key for encryption:
File pubKeyFile = new File("keys/serverPublicKey.der");
byte[] buffer = new byte[(int) pubKeyFile.length()];
DataInputStream in = new DataInputStream(new FileInputStream(pubKeyFile));
in.readFully(buffer);
in.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(new X509EncodedKeySpec(buffer));
and encrypt your password:
String text = password;
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encrypted = cipher.doFinal(text.getBytes());
and save it to your local file system:
FileOutputStream fos = new FileOutputStream("/tmp/encrypted");
fos.write(encrypted);
fos.flush();
fos.close();
The other way for decryption.
Import the private key:
File privKeyFile = new File("keys/clientPrivateKey.der");
byte[] buffer = new byte[(int) privKeyFile.length()];
DataInputStream in = new DataInputStream(new FileInputStream(privKeyFile));
in.readFully(buffer);
in.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(buffer));
read the encrypted file:
File cryptedData = new File("/tmp/encrypted");
buffer = new byte[(int) cryptedData.length()];
in = new DataInputStream(new FileInputStream(cryptedData));
in.readFully(buffer);
in.close();
and decrypt it:
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decrypted = cipher.doFinal(buffer);
String data = new String(decrypted);
System.out.println(data);
You just have to keep your private key secret on the system where your web service is running.
You can provide a web service function which provides the public key to the clients for encryption. Your clients just send the encrypted text string to the web service which decrypts it and authenticate your clients.

KeyFactory.generatePublic from hardcoded X.509 certificate

I need to divide my app to app and extension. So i took an example for Google Play Downloader Library (android-sdk\extras\google\play_apk_expansion\downloader_sample). Project builds fine, but it crashes at
byte[] decodedKey = Base64.decode(encodedPublicKey);
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM); // "RSA"
return keyFactory.generatePublic(new X509EncodedKeySpec(decodedKey));<code>
, on the string keyFactory.generatePublic.
it's from src\com\google\android\vending\licensing\LicenseChecker.java
(android-sdk\extras\google\play_licensing\library\src)
Error:
E/AndroidRuntime(523): java.lang.IllegalArgumentException: java.security.spec.InvalidKeySpecException: java.lang.IllegalArgumentException: Bad sequence size: 3
Key looks like:
private static final String BASE64_PUBLIC_KEY =
"MIIB5TCCAU6gAwIBAgIET45f9zANBgkqhkiG9w0BAQUFADA3MQswCQYDVQQGEwJVUzEQMA4GA1UE" +
"ChMHQW5kcm9pZDEWMBQGA1UEAxMNQW5kcm9pZCBEZWJ1ZzAeFw0xMjA0MTgwNjMyMjNaFw00MjA0" +
"MTEwNjMyMjNaMDcxCzAJBgNVBAYTAlVTMRAwDgYDVQQKEwdBbmRyb2lkMRYwFAYDVQQDEw1BbmRy" +
"b2lkIERlYnVnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDh2IN5HhCp1X+gLaga06VXr/MZ" +
"JpkzhxMdg5yWyOkj50ZDCPywAh8LcNEih9XjYswAXwRHxZpUy9VFqgGcku33AAdHxyK7KK4ge7u5" +
"a7KY11CJhxMUbOGezGldMUTwBA0ZSuObfW402I4Y4ciAsMrOnhZqSTI/tTdAWv6cPTiJQQIDAQAB" +
"MA0GCSqGSIb3DQEBBQUAA4GBAAZ89R7OMtkQnGpE6s/crqUMysAXOaHktrr6mV/4VknoLHWJUsRg" +
"iv34rAFpd1SDg0HS8HklIymcwFkrewwx9MzryYtZEdBjvo2EeTz5u8nxQNz9sqChaya0oSXB/VI8" +
"nZBnoJ+n5Zbj7QfIgG7thrT4+n4pIDO83+E6zVW6RnIh";
If i change key to random string I get:
E/AndroidRuntime(478): java.lang.IllegalArgumentException: java.security.spec.InvalidKeySpecException: java.lang.ClassCastException: com.android.org.bouncycastle.asn1.DERApplicationSpecific cannot be cast to com.android.org.bouncycastle.asn1.ASN1Sequence
I tried to generate key spec and it doesn't cause an error, but that's not what I need :
RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(new BigInteger("12345678", 16), new BigInteger("11", 16));
KeyFactory keyFactory = KeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
return keyFactory.generatePublic(pubKeySpec);
I rechecked generation and export of the certificate. Looks like it's correct.
Key pair generation:
keytool.exe -genkey -keyalg "RSA" -keysize 2048 -v -keystore key2.store -storepass <storepwd> -keypass <keypwd>
, export
keytool.exe -export -rfc -storepass <storepwd> -keystore key2.store -file key222.cer
It's debug code. I'll use public key from Google Play in release.
What is the correct way to hardcode x.509 certificate and use it in app?
That's not a valid encoded public key, it appears to be an Android debug certificate. Note that public key != certificate, although a certificate does include a public key. Also note that any two random BigInteger's do not constitute a valid key pair (they have to be prime, at least). To parse an actual certificate you would need something like this:
FileInputStream fis = new FileInputStream(filename);
BufferedInputStream bis = new BufferedInputStream(fis);
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(bis);
What exactly are you trying to do?

Categories