A 3rd-party-app, which uses OASIS Web Services Security (WSS) for their SOAP authorization, provided me with following two pem files:
ws-client.pem
-----BEGIN CERTIFICATE-----
VQQKDAxCZWFyaW5nUG9pbnQxFDASBgNVBAcMC0TDvHNzZWxkb3JmMQkwBwYDVQQI
[..]
CCsGAQUFBwMCBggrBgEFBQcDCDANBgkqhkiG9w0BAQsFAAOCAQEAY8MiqxLIE+dQ
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
XKJyj28HAgMBAAECggEATF3hM/l2mIDFkJ69kskJUep/+ejl33AXVc9akY7gocLV
[..]
P+htLANmvC29Z5eFgkotZsAQoRi+L4gNsH1kbWHxcYA6YWRCHtpTYsa6/agUnywq
-----END RSA PRIVATE KEY-----
ws-server.pem
-----BEGIN CERTIFICATE-----
cmcxCzAJBgNVBAYTAkRFMB4XDTE3MTExNDExMjA1NloXDTI3MTExNDExMjA1Nlow
[..]
z6RWmsrSfLXGAM5QL4CCDzMth2AP8WzksMzW6dD6qK5/X9yD3g2GRXbHbtnvl6fk
-----END CERTIFICATE-----
The last two days I tried a lot of different approaches, followed several examples.. using curl, nodejs, java etc.. with no success.
This is my latest code:
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.openssl.PEMReader
import java.security.*
import java.security.cert.Certificate
import java.security.cert.CertificateException
import java.security.cert.X509Certificate
class WsSecurityHandler {
private static final char[] KSPASSWORD = null
WsSecurityHandler() {
Security.addProvider(new BouncyCastleProvider())
}
private PrivateKey getPrivateKey() throws IOException {
InputStream pemClientInput = getClass().getClassLoader().getResourceAsStream("pem/ws-client.pem")
Reader reader = new InputStreamReader(pemClientInput, "UTF8")
PEMReader pemReader = new PEMReader(reader)
PrivateKey privateKey = (PrivateKey) pemReader.readObject()
pemReader.close()
return privateKey
}
private X509Certificate getPublicCertificate() throws IOException {
InputStream pemServerInput = getClass().getClassLoader().getResourceAsStream("pem/ws-server.pem")
Reader reader = new InputStreamReader(pemServerInput, "UTF8")
PEMReader pemReader = new PEMReader(reader)
X509Certificate certificate = (X509Certificate) pemReader.readObject()
pemReader.close()
return certificate
}
void initKeyStore() throws IOException {
PrivateKey privateKey = getPrivateKey()
X509Certificate certificate = getPublicCertificate()
Certificate[] chain = new Certificate[1]
chain[0] = certificate
try {
KeyStore ks = KeyStore.getInstance("JKS")
ks.load(null, KSPASSWORD)
ks.setKeyEntry("FooBar", privateKey, KSPASSWORD, chain)
} catch (NoSuchAlgorithmException e) {
e.printStackTrace()
} catch (KeyStoreException e) {
e.printStackTrace()
} catch (CertificateException e) {
e.printStackTrace()
}
}
}
which fails at line
PrivateKey privateKey = (PrivateKey) pemReader.readObject()
with:
Exception in thread "main"
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot
cast object with class
'org.bouncycastle.jce.provider.X509CertificateObject' to class
'java.security.PrivateKey'
I am somehow getting the impression that I misunderstood a big part in this topic.. So it comes to this kind of embarrassing question:
How exactly do I prepare my application to be able to connect to this WSS-SOAP-API?
Related
My generated RSA keys are invalid in external systems.
I've generated RSA Keys using Java.
KeyPairGenerator generator = null;
try {
generator = KeyPairGenerator.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
generator.initialize(2048);
KeyPair pair = generator.generateKeyPair();
StringBuilder sbPrivate = new StringBuilder();
sbPrivate.append("-----BEGIN RSA PRIVATE KEY-----\n");
sbPrivate.append(Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded()));
sbPrivate.append("\n-----END RSA PRIVATE KEY-----\n");
StringBuilder sbPublic = new StringBuilder();
sbPublic.append("-----BEGIN RSA PUBLIC KEY-----\n");
sbPublic.append(Base64.getEncoder().encodeToString(pair.getPublic().getEncoded()));
sbPublic.append("\n-----END RSA PUBLIC KEY-----\n");
In result I've got:
-----BEGIN RSA PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCtundURFaFhoiPmaNbfHUoWQLcohydlwl6riLu64QL/Oyl8V4gqGT2l/OiC2xUMrP8Q+iKWbBaDSr3dmXS5NBiv5QAPJR3GtGE8gP5zRpISeV8uPMvU2Ao5PaEsOazYCITVDAj+b+kDQ25Aq052CTIwyhAnc0OPsAySNYpNgZKpHL0dyY0vHE9hbwN6qc76w3QlC+Oo0swPqoIO3Tk1VnsgV3+5c5vSJwHYTLENfSL8ueVxn79RRYMkIFyFcXxA7LqIp5Byr430f9Zm+jTR2yLSPndMseetDCsEmouzXR8pILMxTFdmhisv6YNPGE5SBh0CUe4thgqT2bqEEbwUscNAgMBAAECggEAFNy1g7sJINO9BnGOumfBseyueUxGX25Jxx+uNj62BrKCwJKxmP5f0XUB3s28XogQMRT+kc6fbNDz/e+L3r13gMQ8w6KCuaLQ0kQ10gOdSl0FX+rd2SsL+x0ztU+BBL1gmi2b8lYFe2/N3A/EOZQJJsq4xLmubC2A6wjwHtbL/p551lvw70hAqIrCV0PZkB+Qja6fycrjC++FFzVAPNmA5PCHxbNX3Jp4t9U3O6SrDq8sbk2F4bApJ/C7hs+JDE5DIzTyU6jxJUf6bxaYOqAfTEkuuM+NrFi29z/si30mL5pA+fuPlkB41xXJhMizPCbOcyHKLnKoc6nIc8Dfin69QQKBgQC5g4Lr59kvhGJVNsxZUJX0TZTJ5c0m/cwWt4cjB46gnJI7a9Quiw9dNxpQO/Hd56geXGWm9IFq87guAUa49+rcWx6KlaKCZqtNKKtFJ3WIon85Yqoyxvnmk/UmCYZZ9Y0Qk+LTaWmNk3tuBkyPOWSSYqmLBejcskweuBfX0p0TOQKBgQDvvJ9qiHEErHiZEEwkizTPLQTQZ6CScTWVX5djKpKWwKHCSEgoH3rYVq/q5Y+pfzO4Mjk30vXuGken9vy5tHvwNV1JEJBt1rWizZBxJakaFrPsyGXj4/Ihf+qojEXxc8FbUHuGjj48IjlVrLID747dYGkgrwBF3hR0Dw6C6frudQKBgCoxqUNytklCeQMctZnS5GhuhNru85xcuWCpWYk0HNKXCgON4PK9Gj9/WvLIz5hDKk/NgClbD1x+FnQQQj+r07VO3EF1C0NRisC/8USHVwo+u2UX9dIaw5gYKvf9zpsDaJROrpabMTDMTJSXxi4FRcStwBYov/ACdaG/CV1DQAqxAoGAEdF8K8feVpGXGRFMB39NOoTHEdJR7fB6IhMLxnQYlxnULAOBUXIdiEzRIG1VgyyQJIWfGRYV70+GueYynH7co1rkBxUzFubZczNCJBa/fc/oKZYvdIOxsGm4gDSE0lBfM7FErRiPaZrFlieSx5YbkTqkNaisJYU/Ee04j+RiIS0CgYBGi63jlyS2YEHlewJUxP29KLIf4G8wGKIAaXi19DX8eU7b+4uniAGwhyvL1Sbe1jcePJgjBA4OXSiUcCDPaNa0rgrVeeW2xeh4ZPmH3tXo1ex5+saIwSDng424K5UPp/rs9oH3CVNe0vHxjrnrQX5VaRcv7ZU+DO6x9VeWMPOXsQ==
-----END RSA PRIVATE KEY-----
-----BEGIN RSA PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArbp3VERWhYaIj5mjW3x1KFkC3KIcnZcJeq4i7uuEC/zspfFeIKhk9pfzogtsVDKz/EPoilmwWg0q93Zl0uTQYr+UADyUdxrRhPID+c0aSEnlfLjzL1NgKOT2hLDms2AiE1QwI/m/pA0NuQKtOdgkyMMoQJ3NDj7AMkjWKTYGSqRy9HcmNLxxPYW8DeqnO+sN0JQvjqNLMD6qCDt05NVZ7IFd/uXOb0icB2EyxDX0i/LnlcZ+/UUWDJCBchXF8QOy6iKeQcq+N9H/WZvo00dsi0j53TLHnrQwrBJqLs10fKSCzMUxXZoYrL+mDTxhOUgYdAlHuLYYKk9m6hBG8FLHDQIDAQAB
-----END RSA PUBLIC KEY-----
But when I'm trying to use this keys here
https://kjur.github.io/jsrsasign/sample/sample-rsasign.html
https://www.devglan.com/online-tools/rsa-encryption-decryption
These keys are invalid.
Where could be a problem?
THe keys are not being written to standard PEM formats: PKCS8 & X.509 respectively.
The following works but the keys need to be tested on https://8gwifi.org/PemParserFunctions.jsp. This shows the PEMs are good.
However, neither of the sites you suggested can process them.
import java.io.StringWriter;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
public class Whatever {
public static void main (String[] args) throws Exception {
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
StringWriter writer = new StringWriter();
try (JcaPEMWriter w = new JcaPEMWriter(writer)) {
w.writeObject(new JcaPKCS8Generator(kp.getPrivate(), null));
}
PemWriter pemWriter = new PemWriter(writer);
pemWriter.writeObject(new PemObject("PUBLIC KEY", kp.getPublic().getEncoded()));
pemWriter.flush();
pemWriter.close();
System.out.println (writer.toString());
}
Output:
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCXHK07oaqx8fnY
r3UbfUS6HRXQFRvQ0J8qqzgq+UH4ZqtgxV44ciSOwzL65E2aZrixXxB+s7Kbbw1q
R0oUNvay8QhMlmwUZwXjCZbeNbQI8LXoXSU1l9xx2GZ7BS3/huFGHSyGzjrSYJdJ
cZKOYij26aCPIx7VYEeIUmPAbkCA1VVUhaOic81aQAdhqrKjqpBcYTwYW4YF2zcy
Dx8YLrRLJbjFzM94eg9oErqIsptyZ83daoNytVTbijzDoXAmkHrx58NfZnuJ0JfH
UKGZiMlt6fBAqDR3+Dls6hemJA+VxCO2dKBDp2vSGfIDc1mr1kQozFK3JqFINcWI
537vnPWVAgMBAAECggEAA/VAagMFx3k/p/05MMdi8l9afSkJtw+Of7hc4APyhlOw
HPiGdi2H3MUVnoHg23thzo7feHtzS+7Id+vBRQ7HKZrhHVpvnx2EsgnurZ1+p0ug
xCLpG4KBsmoD4yiDUtcBAGG5aG2El709G94cQ9uj2DXN2rnwL+VrR5GQOHqFeNUI
rTKUG4lwCPcvPOvnpdYj2jv4oj4uO2cbmgbZThcl4KdHK/Eo/jHr0UOhiT5J9ocm
RKryYYjEXE/t57tR2e0Rsel74fTmcgNygiixMjKDC1cmqX4R+g67m1gfR+/+SXR8
S9f9VzcwugcTnxIhke3TRta53QgfPNLOidpMM1tLwQKBgQC9faOxEVJ2KTQaAAMw
Nx8bBxhev5mifi+f8d14ERkG7XFb4SzPeUY29oB0KVxDyBwR8rgNars+GpUnquZv
91PVs5fYD3W+HwtOD/UOL0z3UtKnNI8nvtK08ru0PFDVzwzqEapy8dLkmbG556GP
HZ5WVn+8QeTX7GqbSU3xtPp21QKBgQDMJpTMzneQ+GrupU1lzdlD8GKF2RbsZ0Ui
rtIx4UYgIQV0lbvPhneJrGy16woOBUZ7jkCEDXKqofGumwCVfhpjjYzIqPfZzXaa
t5a6l2cLuwt0JnjluwqmIfWf1z+GdqCxgqUwdUgzxcPmzxcHwOCX1YFQQ8WONd6s
Id9DfAFjwQKBgQCLsKhQq11oAD4JgMLY83m52gQsLQEcWfvP5GSI08noYnhz7E61
cEjD0fqmJ6t9yHJxBMaMFYeNY9lbEdCo7+JcovWocNUy3/3cgUT9PP93QBZM7yEt
gq4geOTJHMHWrLlvgLBv5je7EFaFnu1p7MLCESg/ZzBFwWJhsauFKQ6PNQKBgFDc
PzfX15f+LSyVINDf9dxpDD0DvYapaMLSB8Nl/Qagza5d2GPcWOCZAP4VOIhRIpex
wnALe42GU1nbXyHXLtCbslWQR4tnTED/0p3ZdiE5VtIMovorWY5wCP/km+7Acemd
W5yT96M6A9wZzn9tsAezs2J9VXR8ddQsHmh2Z36BAoGBAIkFBge0QbWZGYCr3uk9
K0AhZUekGSzhakqp60XQs5kw8zb+TllCRxtYsQlyaHp1M8AH3Di/Uw+EhBt6h4Uw
fAPCZRg8vdG8Hp26PwXxybZ/M9u7NaKJ0BT4AwKKtZTUxZVxz/kPhdHT+MpoQqJf
JuzuwXVAAcl1GME2OiqkZhww
-----END PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlxytO6GqsfH52K91G31E
uh0V0BUb0NCfKqs4KvlB+GarYMVeOHIkjsMy+uRNmma4sV8QfrOym28NakdKFDb2
svEITJZsFGcF4wmW3jW0CPC16F0lNZfccdhmewUt/4bhRh0shs460mCXSXGSjmIo
9umgjyMe1WBHiFJjwG5AgNVVVIWjonPNWkAHYaqyo6qQXGE8GFuGBds3Mg8fGC60
SyW4xczPeHoPaBK6iLKbcmfN3WqDcrVU24o8w6FwJpB68efDX2Z7idCXx1ChmYjJ
benwQKg0d/g5bOoXpiQPlcQjtnSgQ6dr0hnyA3NZq9ZEKMxStyahSDXFiOd+75z1
lQIDAQAB
-----END PUBLIC KEY-----
Results from 8gwifi.org
Private key:
Private Key algo RSA
Private Format PKCS#8
ASN1 Dump
RSA Private CRT Key [f9:3d:bb:ce:14:eb:b6:f4:68:c0:69:54:a8:47:da:56:78:6d:d2:ad]
modulus: 971cad3ba1aab1f1f9d8af751b7d44ba1d15d0151bd0d09f2aab382af941f866ab60c55e3872248ec332fae44d9a66b8b15f107eb3b29b6f0d6a474a1436f6b2f1084c966c146705e30996de35b408f0b5e85d253597dc71d8667b052dff86e1461d2c86ce3ad260974971928e6228f6e9a08f231ed56047885263c06e4080d5555485a3a273cd5a400761aab2a3aa905c613c185b8605db37320f1f182eb44b25b8c5cccf787a0f6812ba88b29b7267cddd6a8372b554db8a3cc3a17026907af1e7c35f667b89d097c750a19988c96de9f040a83477f8396cea17a6240f95c423b674a043a76bd219f2037359abd64428cc52b726a14835c588e77eef9cf595
public exponent: 10001
Public key:
Algo RSA
Format X.509
ASN1 Dump
RSA Public Key [a6:cc:21:c6:b2:98:92:7a:96:d6:5b:ac:38:32:0c:dc:a3:1c:42:e5]
modulus: 9c3eac15e0897fd6d1ecf1ff622b0fc0278f31496d10babf869fc39b8d3d5d4ff211b8014c8ff03149c7fb68ee39361e0a2ace075d1b73f42b15585a2d5de01d6caf48bf96bf4b9f75679988b5c09c808d75d889090bce53ee6c78d5283f0e102af14042064ba20b235a6dae6a32529d21ac9150ade3c81a595ac888ee0e419564493c57ad43649de9d727d6f66d76a6fc1b75c9ad44d6dd7f6ff21367fd375908df1b05e072da98cadbc01c2defe78d6cf68e77276b969ffa4e049736d39f007795b2cea3d11f303581b97e5e29cab3ce57e54e4f030262542b12c82164346c099a6b823edac78a9c198b96b1e637eb6fbd4f962963d9539776b476cd4ec539
public exponent: 10001
There is a dependency on Bouncy Castle as follows:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.56</version>
</dependency>
I'm trying to run the following program:
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.PEMKeyPair;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.KeyPair;
import java.security.KeyFactory;
import java.io.StringReader;
import javax.crypto.Cipher;
import java.util.Base64;
import java.security.interfaces.RSAPrivateKey;
public class Test
{
public static void main(String[] args) throws Exception
{
Security.addProvider(new BouncyCastleProvider());
String key = "-----BEGIN RSA PRIVATE KEY-----" +
"MIIBOgIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu" +
"KUpRKfFLfRYC9AIKjbJTWit+CqvjWYzvQwECAwEAAQJAIJLixBy2qpFoS4DSmoEm" +
"o3qGy0t6z09AIJtH+5OeRV1be+N4cDYJKffGzDa88vQENZiRm0GRq6a+HPGQMd2k" +
"TQIhAKMSvzIBnni7ot/OSie2TmJLY4SwTQAevXysE2RbFDYdAiEBCUEaRQnMnbp7" +
"9mxDXDf6AU0cN/RPBjb9qSHDcWZHGzUCIG2Es59z8ugGrDY+pxLQnwfotadxd+Uy" +
"v/Ow5T0q5gIJAiEAyS4RaI9YG8EWx/2w0T67ZUVAw8eOMB6BIUg0Xcu+3okCIBOs" +
"/5OiPgoTdSy7bcF9IGpSE8ZgGKzgYQVZeN97YE00" +
"-----END RSA PRIVATE KEY-----";
String ciphertext = "L812/9Y8TSpwErlLR6Bz4J3uR/T5YaqtTtB5jxtD1qazGPI5t15V9drWi58colGOZFeCnGKpCrtQWKk4HWRocQ==";
// load the private key
PEMParser pemParser = new PEMParser(new StringReader(key));
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair keyPair = converter.getKeyPair((PEMKeyPair) pemParser.readObject());
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPrivateCrtKeySpec privateKeySpec = keyFactory.getKeySpec(keyPair.getPrivate(), RSAPrivateCrtKeySpec.class);
RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(privateKeySpec);
// load the ciphertext
byte[] cipherBytes = Base64.getDecoder().decode(ciphertext);
// perform the actual decryption
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding", "BC");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] plaintext = cipher.doFinal(cipherBytes);
}
}
It was able to compile without issue but when I try to run it I get the following error:
Exception in thread "main" org.bouncycastle.openssl.PEMException: unable to convert key pair: null
at org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter.getKeyPair(Unknown Source)
at Test.main(MyTest.java:35)
Caused by: java.lang.NullPointerException
... 2 more
So I guess getKeyPair doesn't like (PEMKeyPair) pemParser.readObject(). Well that's what I got from Get a PrivateKey from a RSA .pem file...
I had a similar issue and was able to solve it by altering the key from
-----BEGIN RSA PRIVATE KEY-----
.....content here.....
-----END RSA PRIVATE KEY-----
to:
-----BEGIN EC PRIVATE KEY-----
.....content here.....
-----END EC PRIVATE KEY-----
Since you are working with an RSA instance and not an elliptic curve (EC) this might not be the source of your problems but maybe it helps someone.
I have a RSA Key and a X.509 Certificate which I use for SSL connections.
The key and certificate are stored in files in PEM format (generated by OpenSSL) and used in an Apache HTTP server environment.
Is there an easy way to validate if the key matches the certificate using only Java code (without executing the openssl binary and parsing the output), for example by using Java security and/or Bouncycastle library methods?
The following code compares the SHA-1 over the modulus within the public and private key. The modulus should be unique for each pair (unless you key pair generation mechanism or random generator is broken of course).
Note that the following code requires the key to be in unencrypted PKCS#8 format. It may be better to use PKCS#12 instead and load the binary PKCS#12 file in a KeyStore (providing the password).
openssl pkcs8 -topk8 -in key.pem -out keypk8.pem -nocrypt
And finally the Java code:
import static org.bouncycastle.util.encoders.Hex.toHexString;
import java.io.ByteArrayInputStream;
import java.io.FileReader;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
public class CompareCertAndKey {
/**
* Checks if the certificate and RSA private key match.
*
* #param args the path to the certificate file in args[0] and that of the private key in args[1]
*/
public static void main(String[] args) {
try {
final PemReader certReader = new PemReader(new FileReader(args[0]));
final PemObject certAsPemObject = certReader.readPemObject();
if (!certAsPemObject.getType().equalsIgnoreCase("CERTIFICATE")) {
throw new IllegalArgumentException("Certificate file does not contain a certificate but a " + certAsPemObject.getType());
}
final byte[] x509Data = certAsPemObject.getContent();
final CertificateFactory fact = CertificateFactory.getInstance("X509");
final Certificate cert = fact.generateCertificate(new ByteArrayInputStream(x509Data));
if (!(cert instanceof X509Certificate)) {
throw new IllegalArgumentException("Certificate file does not contain an X509 certificate");
}
final PublicKey publicKey = cert.getPublicKey();
if (!(publicKey instanceof RSAPublicKey)) {
throw new IllegalArgumentException("Certificate file does not contain an RSA public key but a " + publicKey.getClass().getName());
}
final RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
final byte[] certModulusData = rsaPublicKey.getModulus().toByteArray();
final MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
final byte[] certID = sha1.digest(certModulusData);
final String certIDinHex = toHexString(certID);
final PemReader keyReader = new PemReader(new FileReader(args[1]));
final PemObject keyAsPemObject = keyReader.readPemObject();
if (!keyAsPemObject.getType().equalsIgnoreCase("PRIVATE KEY")) {
throw new IllegalArgumentException("Key file does not contain a private key but a " + keyAsPemObject.getType());
}
final byte[] privateKeyData = keyAsPemObject.getContent();
final KeyFactory keyFact = KeyFactory.getInstance("RSA");
final KeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyData);
final PrivateKey privateKey = keyFact.generatePrivate(keySpec);
if (!(privateKey instanceof RSAPrivateKey)) {
throw new IllegalArgumentException("Key file does not contain an X509 encoded private key");
}
final RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) privateKey;
final byte[] keyModulusData = rsaPrivateKey.getModulus().toByteArray();
final byte[] keyID = sha1.digest(keyModulusData);
final String keyIDinHex = toHexString(keyID);
System.out.println(args[0] + " : " + certIDinHex);
System.out.println(args[1] + " : " + keyIDinHex);
if (certIDinHex.equalsIgnoreCase(keyIDinHex)) {
System.out.println("Match");
System.exit(0);
} else {
System.out.println("No match");
System.exit(-1);
}
} catch (Exception e) {
e.printStackTrace(System.err);
System.exit(-2);
}
}
}
Thank you very much for the above code snippet. Its working for me with bouncycastle version 1.51
<bcprov-jdk15on-version>1.51</bcprov-jdk15on-version>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>${bcprov-jdk15on-version}</version>
</dependency>
Many Thanks for your code snippet.
I need to be able to read in Java a S/MIME file generated from OpenSSL. The openssl command looks like
openssl smime -encrypt -in test_message.txt -out test_out.pem -outform pem certificate.pem
This creates a file that looks like
-----BEGIN PKCS7-----
MIIBkwYJKoZIhvcNAQcDoIIBhDCCAYACAQAxggE+MIIBOgIBADAiMBoxGDAWBgNV
BAMTD0ZvcmRTRE5TZWN1cml0eQIEUw5nyTANBgkqhkiG9w0BAQEFAASCAQBK9wAV
wAXRM7oMWJz113VX7Tb/MslQatHZH2oaX5XJnIJrvnTv9T3irQR9H+pegh1q6OZv
v4Mz/QBFO2iq4tv6xGHE8hl0ZdmNCUdTN41qutZP2+N1YrKi9QLmnuAi3BkEzzeW
YTGvE8xGsjNlTLOjz7P5lZdCWpGJmdPeUDP0IYsOsuMspPcujyOdA5y++y6x90WF
J3ovzPhCRU7303EhdQ1hHse8KTen56XZflL3zhnT2KGtN/Pq3aZ1MVhmLZ+EZuUF
ygxlwCXi3FUx7P35XZAGpTUPFM2sz5p+oSrcxA+fsUgiMb96tfaXZLYE753mA2tZ
WfCRd86nzJsVE/YhMDkGCSqGSIb3DQEHATAaBggqhkiG9w0DAjAOAgIAoAQIqd23
FXgqdaSAEHLeYH0LG9G+UfCBxQOalIE=
-----END PKCS7-----
I am currently using BouncyCastle to try to read in test_out.pem,
....
MimeMessage mimeMessage = new MimeMessage(session, new FileInputStream("test_out.pem"));
SMIMEEnveloped smimeEnveloped = new SMIMEEnveloped(mimeMessage);
...
but I can't figure out how to make it accept a message without the MIME headers, as I get the following error:
java.lang.NullPointerException: null
at org.bouncycastle.cms.CMSEnvelopedData.<init>(Unknown Source) ~[bcpkix-jdk15on-1.50.jar:1.50.0]
at org.bouncycastle.cms.CMSEnvelopedData.<init>(Unknown Source) ~[bcpkix-jdk15on-1.50.jar:1.50.0]
at org.bouncycastle.mail.smime.SMIMEEnveloped.<init>(Unknown Source) ~[bcmail-jdk15on-1.50.jar:1.50.0]
What would be the best way to read a PEM (or DER) formatted file like this in and be able to decrypt it with a java.security.PrivateKey?
Here's how you can do the decryption using BouncyCastle 1.57 (inspired by this article):
import org.bouncycastle.cms.CMSEnvelopedData;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.KeyTransRecipientInformation;
import org.bouncycastle.cms.RecipientInformation;
import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
import org.bouncycastle.cms.jcajce.JceKeyTransRecipient;
import org.bouncycastle.util.encoders.Base64;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Collection;
public class PKCS7Decryptor {
private PrivateKey privateKey;
public PKCS7Decryptor(String privateKeyStr) {
try {
byte[] privateKeyData = extractRawData(privateKeyStr, "PRIVATE KEY");
PKCS8EncodedKeySpec kspec = new PKCS8EncodedKeySpec(privateKeyData);
KeyFactory kf = KeyFactory.getInstance("RSA");
privateKey = kf.generatePrivate(kspec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new RuntimeException("Unable to parse private key");
}
}
public String decrypt(String encryptedText) throws CMSException {
byte[] data = extractRawData(encryptedText, "PKCS7");
CMSEnvelopedData envelopedData = new CMSEnvelopedData(data);
Collection<RecipientInformation> recipients = envelopedData.getRecipientInfos().getRecipients();
KeyTransRecipientInformation recipientInfo = (KeyTransRecipientInformation) recipients.iterator().next();
JceKeyTransRecipient recipient = new JceKeyTransEnvelopedRecipient(privateKey);
return new String(recipientInfo.getContent(recipient));
}
private byte[] extractRawData(String text, String dataType) {
return Base64.decode(text
.replace(String.format("-----BEGIN %s-----", dataType), "")
.replace(String.format("-----END %s-----", dataType), ""));
}
}
Some explanation:
In the class constructor the private key is converted to a proper format
Headers and footers (like "-----BEGIN PKCS7-----") are removed and the content is base64-decoded
I have file.pfx file and also have a private key. How can I read the certificate in file.pfx in Java?
I have used this code:
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
import javax.crypto.SecretKey;
import javax.security.auth.callback.*;
//These packages I have used.
public String readFile(String fn) {
String thisLine, ret = "";
KeyStore ks = KeyStore.getInstance("pkcs12", "SunJSSE");
ks.load(new FileInputStream(fn),"password".toCharArray());
try {
Key key = ks.getKey("1", "password".toCharArray());
Certificate[] cc = ks.getCertificateChain("1");
X509Certificate certificate1 = (X509Certificate) cc[0];//Here it throws java.lang.NullPointerException
ret += certificate1.getNotAfter();
ret += certificate1.getNotBefore();
} catch(Exception e) {
ret = "Cannot load, exception!";
}
return ret;
}
Try This Code for Reading .pfx file:-
public void checkExpire() {
try {
KeyManagerFactory kmf = javax.net.ssl.KeyManagerFactory.getInstance("SunX509");
KeyStore keystore = KeyStore.getInstance("PKCS12");
char[] password= "yourfilepassword".toCharArray();
keystore.load(new FileInputStream("filepath\filename.pfx"),password);
//keystore.load(new FileInputStream(certificate), password);
kmf.init(keystore, psswd);
Enumeration<String> aliases = keystore.aliases();
while(aliases.hasMoreElements()){
String alias = aliases.nextElement();
if(keystore.getCertificate(alias).getType().equals("X.509")){
Date expDate = ((X509Certificate) keystore.getCertificate(alias)).getNotAfter();
Date fromDate= ((X509Certificate) keystore.getCertificate(alias)).getNotBefore();
System.out.println("Expiray Date:-"+expDate );
System.out.println("From Date:-"+fromDate);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
You are getting an exception because your keystore (i.e. the PKCS #12 file) does not contain a certificate chain with the alias you have provided.
Key key = ks.getKey("1", "shalimar1234".toCharArray());
Certificate[] cc = ks.getCertificateChain("1"); // this is returning null
It's quite plausible your key object is null too, but you don't appear to use the object at all.
To understand what aliases are available in your file, trying looking at the strings returned from KeyStore.aliases().
Here's a link to a forum question on the subject of opening and reading a .PFX file using Java code.
To summarize what's in the link, you should be able to open the Key-store as you would with a normal JKS, but with a slight difference, pass the Key-store type as pcks12 and the provider as SunJSSE.
try (FileInputStream stream = new FileInputStream("C:/store.pfx")) {
KeyStore store = KeyStore.getInstance("pkcs12", "SunJSSE");
store.load(stream, "password".toCharArray());
Enumeration<String> aliases = store.aliases();
while (aliases.hasMoreElements()) {
System.err.println(aliases.nextElement());
}
X509Certificate certificate = (X509Certificate)store.getCertificate("alias");
System.err.println(certificate.getNotAfter());
System.err.println(certificate.getNotBefore());
System.err.println(certificate.toString());
}
Another helpful note is that you might wanna consider using and referring to the BouncyCastle Provider, it is the most complete implementation out there in my humble opinion.