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
Related
I need to verify the leaf certificate using itsparent certificate.
I got 2 certificate from bing.com, when I used the Java API it success.
But in my App, I have not got the whole certificate, Only can got the Values of the part of the certificate. When I pick up the Values of the certificates and verify by myself , it failed.
Please help me find out what's wrong with my code, here is my code:
package net.laixiong;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.net.ssl.HttpsURLConnection;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
public class certVerify {
private static Certificate CACert;
private static Certificate[] certs;
private static String TBSCertificate = "30820b42a003020102021312001f72f70a0aa155962ab9cb0000001f72f7300d06092a864886f70d01010b0500304f310b3009060355040613025553311e301c060355040a13154d6963726f736f667420436f72706f726174696f6e3120301e060355040313174d6963726f736f66742052534120544c53204341203031301e170d3231313232323031333430375a170d3232303632323031333430375a3017311530130603550403130c7777772e62696e672e636f6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100961baf1cb8345ab7890d195c0497b18a74dad2c684a2b9e2c5abe101712246096505fdff105dc1031f612747efddd975fb7fead9f491193d5b46832d413fe48c35a5eac160114f7e549f2e9d491a3e41b6bafa0fee55481052031f4cabfb5a0144e598b5674c91da989b7ec916e288b9fafb021fe8ff21c36fc169eedb5f908e8b02f9456ca8309cf42f12935852dac787f1df0b8544a8a7055349ca1a269ad2435e51bae1485793081b7f899d7c8d5b406bb3bab38967c32fd7b308422953fa00671766fb2b1e47f8bc491778176c98c07e83c5f7c354b510aa418cc0200dc9ac72b14e18b600b0e69687a31935ee0fdae596ff036844cfadc87433dcd4d2cd0203010001a3820965308209613082017e060a2b06010401d6790204020482016e0482016a01680076002979bef09e393921f056739f63a577e5be577d9c600af8f94d5d265c255dc7840000017ddfcf67d900000403004730450221008251d39ea76085d5428255a300a745caa1ec972c263e74bc7d9daa235e773b81022015d2d03176b33d1d637b34427530903caf207d64d0829c72d3e8cbb8fac0bc4a0077005581d4c2169036014aea0b9b573c53f0c0e43878702508172fa3aa1d0713d30c0000017ddfcf68eb0000040300483046022100c438228eadeea0cae0ca89481281178818863449889ca664e47d6bef472256ca022100bdad9c294d8a15135f1764169e8f19d825291c9aad854e6e4d7c8cd97db1b66d00750051a3b0f5fd01799c566db837788f0ca47acc1b27cbf79e88429a0dfed48b05e50000017ddfcf68f20000040300463044022077c61a666950923aa2ff338d27b115b5aa24e3a602651eb7da06e994569b239202202164db2e6b805640b8873b05e72f53ee7b4c008dfd19846cb726da194bdc41e3302706092b060104018237150a041a3018300a06082b06010505070301300a06082b06010505070302303e06092b06010401823715070431302f06272b060104018237150887da867583eed90182c9851b81b59e6185f4eb60815d85868e4187c2985002016402012730818706082b06010505070101047b3079305306082b060105050730028647687474703a2f2f7777772e6d6963726f736f66742e636f6d2f706b692f6d73636f72702f4d6963726f736f6674253230525341253230544c53253230434125323030312e637274302206082b060105050730018616687474703a2f2f6f6373702e6d736f6373702e636f6d301d0603551d0e041604149ef980a5b4eca659cefeb11b21e869aa01547ced300e0603551d0f0101ff0404030204b03082056d0603551d110482056430820560820c7777772e62696e672e636f6d8210646963742e62696e672e636f6d2e636e82132a2e706c6174666f726d2e62696e672e636f6d820a2a2e62696e672e636f6d820862696e672e636f6d821669656f6e6c696e652e6d6963726f736f66742e636f6d82132a2e77696e646f77737365617263682e636f6d8219636e2e69656f6e6c696e652e6d6963726f736f66742e636f6d82112a2e6f726967696e2e62696e672e636f6d820d2a2e6d6d2e62696e672e6e6574820e2a2e6170692e62696e672e636f6d821865636e2e6465762e7669727475616c65617274682e6e6574820d2a2e636e2e62696e672e6e6574820d2a2e636e2e62696e672e636f6d821073736c2d6170692e62696e672e636f6d821073736c2d6170692e62696e672e6e6574820e2a2e6170692e62696e672e6e6574820e2a2e62696e67617069732e636f6d820f62696e6773616e64626f782e636f6d8216666565646261636b2e6d6963726f736f66742e636f6d821b696e736572746d656469612e62696e672e6f66666963652e6e6574820e722e6261742e62696e672e636f6d82102a2e722e6261742e62696e672e636f6d82122a2e646963742e62696e672e636f6d2e636e820f2a2e646963742e62696e672e636f6d820e2a2e73736c2e62696e672e636f6d82102a2e61707065782e62696e672e636f6d82162a2e706c6174666f726d2e636e2e62696e672e636f6d820d77702e6d2e62696e672e636f6d820c2a2e6d2e62696e672e636f6d820f676c6f62616c2e62696e672e636f6d821177696e646f77737365617263682e636f6d820e7365617263682e6d736e2e636f6d82112a2e62696e6773616e64626f782e636f6d82192a2e6170692e74696c65732e646974752e6c6976652e636f6d820f2a2e646974752e6c6976652e636f6d82182a2e74302e74696c65732e646974752e6c6976652e636f6d82182a2e74312e74696c65732e646974752e6c6976652e636f6d82182a2e74322e74696c65732e646974752e6c6976652e636f6d82182a2e74332e74696c65732e646974752e6c6976652e636f6d82152a2e74696c65732e646974752e6c6976652e636f6d820b33642e6c6976652e636f6d82136170692e7365617263682e6c6976652e636f6d8214626574612e7365617263682e6c6976652e636f6d8215636e7765622e7365617263682e6c6976652e636f6d820c6465762e6c6976652e636f6d820d646974752e6c6976652e636f6d821166617265636173742e6c6976652e636f6d820e696d6167652e6c6976652e636f6d820f696d616765732e6c6976652e636f6d82116c6f63616c2e6c6976652e636f6d2e617582146c6f63616c7365617263682e6c6976652e636f6d82146c7334642e7365617263682e6c6976652e636f6d820d6d61696c2e6c6976652e636f6d82116d6170696e6469612e6c6976652e636f6d820e6c6f63616c2e6c6976652e636f6d820d6d6170732e6c6976652e636f6d82106d6170732e6c6976652e636f6d2e6175820f6d696e6469612e6c6976652e636f6d820d6e6577732e6c6976652e636f6d821c6f726967696e2e636e7765622e7365617263682e6c6976652e636f6d8216707265766965772e6c6f63616c2e6c6976652e636f6d820f7365617263682e6c6976652e636f6d8212746573742e6d6170732e6c6976652e636f6d820e766964656f2e6c6976652e636f6d820f766964656f732e6c6976652e636f6d82157669727475616c65617274682e6c6976652e636f6d820c7761702e6c6976652e636f6d82127765626d61737465722e6c6976652e636f6d82137765626d6173746572732e6c6976652e636f6d82157777772e6c6f63616c2e6c6976652e636f6d2e617582147777772e6d6170732e6c6976652e636f6d2e61753081b00603551d1f0481a83081a53081a2a0819fa0819c864d687474703a2f2f6d7363726c2e6d6963726f736f66742e636f6d2f706b692f6d73636f72702f63726c2f4d6963726f736f6674253230525341253230544c53253230434125323030312e63726c864b687474703a2f2f63726c2e6d6963726f736f66742e636f6d2f706b692f6d73636f72702f63726c2f4d6963726f736f6674253230525341253230544c53253230434125323030312e63726c30570603551d200450304e304206092b0601040182372a013035303306082b060105050702011627687474703a2f2f7777772e6d6963726f736f66742e636f6d2f706b692f6d73636f72702f6370733008060667810c010201301f0603551d23041830168014b5760c3011cec792424d4cc75c2cc8a90ce80b64301d0603551d250416301406082b0601050507030106082b06010505070302";
private static String pkInfoOfString = "30820222300d06092a864886f70d01010105000382020f003082020a0282020100aa6277cf9a63b20684f39036f499f31451abea950a3b4606fd11411ffe5b0658c9386e08fc4f4448cd3aa4f7bd1ea2e295b8be5120c5bfb270635d780c43c029cd64490996daafcefd055f2b2a91e8016e2e189b2c9cd0017f69f5ee3f53885cba056cbe2215671482f22cd2be5b6337ccaf6085e8966b6b8008a86ebe009c6b9570fce41812b11d1bb2c11331673334e625c9625b58827576f2fef23f3b16dfaa4283e3326d9b8e4326f0bd0e1fa1a73aaf2cc88ae6ea3ff9a5d2258f92aa1a08129cfeac4ac7c3eb8094ab8716d12349e7a4bbc791dfe679343f414aa73a26d2ea6f46e33873e6e5d491ae0b789e78a5ef96e373d8f79565e905bf4f5cff52a7f9cf08afa74d0999c071a3527aa53bd79b015403e3b662b05a279c30268eb64d56a117177a7b95a107ac5331b6d62e0fcd4174ecf101b2fd45bffc31e146423136431eb9aa055f847f91b18bae0fd754c3fdf064086ad39c8eea7934ec033d73e01b36d46811c75970b0877cc0dc6e45ca36ce43267702a9700de8b857544442c3fbac1b632608c2d2231f7f930b7c6f08549a2b4e5dce9fa53ed2985bd102dbf183ce3052483863f1b1fbed23d33e92b5278dd04273d79d236871ba595e0752a6964dbf7c4e6f742205c0538016d8604e97314f894e4863d8edf9e5c2d90eb20bf6694cbd4b01c9cbdd06bf3a02eb1cdd308b0d4a1460f9d5644f4344a1ed0203010001";
private static String sigValue = "35ff650af97d6a3eea1ee00f95ecae0ebd0cf43d8b3237a29bea282fb427571c3972eb0121f72d1b4a815f63c47befbe562522806834bfa40a6ac27b5d0de1b3a91f4c01cc4d78225eafcda5679689e11655dae04a88a13e665b726bc3eb0097811e2400645e53ac8a3e98bae9d492539ca5736802fbafa8a32e3a2cf49ede7a290361ef677036baf5d8dd3997297f4ad82c69b075280522deaec748fa071251986caa7ce95b4ec0ba832eaaecc2da75266fc1c534aaa4a90e2d825b87b54e74ea3fec8279d0cd352de799d3935261c0288196d7eb3121b3f8686b2e03bcaff0e2305f263e8fb2d33b28614f47d5da1432675dca8234e59f888e1cf0234891a98953b3279ffffead9d5a8efce6ab9ff7b964af24f1458e1ace76736a4024c8c83fa76995fb2bc1cee36f74db88cdbaa86a6fa5fea7acb83796a84ba73b8f2337d7f8f79dde1b1602a9bfde4fc09f7914f8187763c7baa04c602de5db5a9d6f9b6e0d805856b8e0cf3d20b598432f511e99532720c14ed74a1a86798b4acd7eddb7d107f8e8405eafffd446cf67fc78d68e6e0222415cb8d8bf1ead35f42c1bd200039623595ba3bbfc4b5ec25681354293b860bb5d90151fbd2f9f68e68930c45bb90f62a065bd813d9146709e6cdc1d10656284a83079c8f0e6a354660436f176b26f3a2c14298ec9551f2daa5979ba7cfca7059afe161958761f50ad241643";
static void sigVerify1() throws IOException, CertificateEncodingException {
URL destinationURL = new URL("https://www.bing.com");
HttpsURLConnection con = (HttpsURLConnection) destinationURL.openConnection();
con.connect();
certs = con.getServerCertificates();
X509Certificate x509cert = (X509Certificate) certs[0];
CACert = certs[1];
byte[] b = x509cert.getTBSCertificate();
b[0] = (byte) ~b[0];
try {
PublicKey pkOfCa = CACert.getPublicKey();
x509cert.verify(pkOfCa);
System.out.println("true");
} catch (GeneralSecurityException e2) {
System.out.println("false");
}
}
static void sigVerify2() throws InvalidKeySpecException {
try {
PublicKey pkOfCA = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(DatatypeConverter.parseHexBinary(pkInfoOfString)));
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pkOfCA);
byte[] tbsHashValue1 = cipher.doFinal(DatatypeConverter.parseHexBinary(sigValue));
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] tbsHashValue2 = messageDigest.digest(DatatypeConverter.parseHexBinary(TBSCertificate));
System.out.println(Arrays.equals(tbsHashValue1, tbsHashValue2));
} catch ( NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws CertificateEncodingException, NoSuchAlgorithmException, IOException, SignatureException, InvalidKeyException, InvalidKeySpecException {
sigVerify1();
sigVerify2();
}
}
//C:\Java\jdk1.8.0_211\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJIDEACommunityEdition2020.3.1\lib\idea_rt.jar=56057:C:\Program Files\JetBrains\IntelliJIDEACommunityEdition2020.3.1\bin" -Dfile.encoding=UTF-8 -classpath C:\Java\jdk1.8.0_211\jre\lib\charsets.jar;C:\Java\jdk1.8.0_211\jre\lib\deploy.jar;C:\Java\jdk1.8.0_211\jre\lib\ext\access-bridge-64.jar;C:\Java\jdk1.8.0_211\jre\lib\ext\cldrdata.jar;C:\Java\jdk1.8.0_211\jre\lib\ext\dnsns.jar;C:\Java\jdk1.8.0_211\jre\lib\ext\jaccess.jar;C:\Java\jdk1.8.0_211\jre\lib\ext\jfxrt.jar;C:\Java\jdk1.8.0_211\jre\lib\ext\localedata.jar;C:\Java\jdk1.8.0_211\jre\lib\ext\nashorn.jar;C:\Java\jdk1.8.0_211\jre\lib\ext\sunec.jar;C:\Java\jdk1.8.0_211\jre\lib\ext\sunjce_provider.jar;C:\Java\jdk1.8.0_211\jre\lib\ext\sunmscapi.jar;C:\Java\jdk1.8.0_211\jre\lib\ext\sunpkcs11.jar;C:\Java\jdk1.8.0_211\jre\lib\ext\zipfs.jar;C:\Java\jdk1.8.0_211\jre\lib\javaws.jar;C:\Java\jdk1.8.0_211\jre\lib\jce.jar;C:\Java\jdk1.8.0_211\jre\lib\jfr.jar;C:\Java\jdk1.8.0_211\jre\lib\jfxswt.jar;C:\Java\jdk1.8.0_211\jre\lib\jsse.jar;C:\Java\jdk1.8.0_211\jre\lib\management-agent.jar;C:\Java\jdk1.8.0_211\jre\lib\plugin.jar;C:\Java\jdk1.8.0_211\jre\lib\resources.jar;C:\Java\jdk1.8.0_211\jre\lib\rt.jar;C:\myIDEAWorks\mySpecial\target\classes;C:\Users\lihong\.m2\repository\redis\clients\jedis\3.6.1\jedis-3.6.1.jar;C:\Users\lihong\.m2\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;C:\Users\lihong\.m2\repository\org\apache\commons\commons-pool2\2.9.0\commons-pool2-2.9.0.jar;C:\Users\lihong\.m2\repository\net\sf\opencsv\opencsv\2.3\opencsv-2.3.jar;C:\Users\lihong\.m2\repository\org\apache\httpcomponents\httpcore\4.4.4\httpcore-4.4.4.jar;C:\Users\lihong\.m2\repository\org\postgresql\postgresql\9.4.1208.jre7\postgresql-9.4.1208.jre7.jar net.laixiong.certVerify
// true
// false
Added after I resolve the problem.
Thanks #dave_thompson_085, I found the corrent method in Signature API , my problem been resolved.
So the code like this :
PublicKey pkOfCA = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(DatatypeConverter.parseHexBinary(pkInfoOfString)));
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(pkOfCA);
sig.update(DatatypeConverter.parseHexBinary(TBSCertificate));
System.out.println(sig.verify(DatatypeConverter.parseHexBinary(sigValue)));
Thanks again.
Basically dupe How to use x509 public key to verify a signed/hashed Alexa request body? and more (across several Stacks) linked at https://crypto.stackexchange.com/questions/87006/why-is-data-signed-with-sha256-rsa-pkcs-and-digest-signed-with-rsa-pkcs-differen/#87022 .
First of all, the better way to verify a signature 'manually' is to use the Signature API as designed:
PublicKey pkOfCA = KeyFactory...generatePublic... // as now
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initSign(pkOfCA);
sig.update(DatatypeConverter.parseHexBinary(TBSCertificate));
System.out.println(sig.verify(DatatypeConverter.parseHexBinary(sigValue)));
However, if you insist on doing it with the 'backwards Cipher' method, you must do the ASN.1 DigestInfo DER encoding or decoding yourself; this amounts to adding or checking and removing a prefix that depends only on the hash algorithm used, as shown in e.g. PKCS1v2.2 = RFC 8017 section 9.2 'Notes'. Specifically, either compare the 'decrypted' value to the concatenation of the appropriate prefix plus the actual hash value, or remove that prefix from the 'decrypted' value and compare only the remainder to the hash value.
I want to encrypt my password in angular side before sending it to Springboot API. My idea is to do:-
Generate public key and private key in java
Do a base64 encoding on both public key and private key
Use encoded public key in node (angular) side, this will be used for encryption
Use encoded private key in springboot rest api, this will be used for decryption
Below is my java code which generates, encrypts and decrypts password
RSAKeyPairGenerator.java
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
public class RSAKeyPairGenerator {
private static String privateKeyString = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALyR1WRqVBuvdmXmgrcZ4VMY4hIFTYt63NGEN9atlgnngNliMasvzBofnjRtk5mukezUKujtON6mPnAjKJoEDdiaw3Rk0hKvgW5MszyUBE5bLThexVLbi+nQYSf2GSvd0fjdZn6KjhgVOdbYe+JduU/iPD3/Ti5p+w3MewgZpy2RAgMBAAECgYAqWdqCXfsb6LF/u2C6PN7Faf5EK9q5q9NyXu6nkX70JIFk0U/0cZy2dUlz3vRafMGbXh9xBu5R2yaEyvCwfp6ZF26A8MbS/IaI53LMKmyclhES2f3tZLKUPkg6fntz+e6e/6oSDdQPHP3J2QrpuwzyW+UZJQmmYYj7f9sq/+dawQJBAOnkHWkLuIZNIgLzV0TuHA6fcOXrqrSlS1/Q0qLyaQEZOObocXDvWaCSP+8RXiqZSwAnMJbrGHQA5ULXLWI4GjkCQQDOZP6bfn2VV3m8JSlYL9Uz4TrRYb8Qe3/mohhMsyDB7Ua/6d9BV7JZgbYiXP0utobKVWLxD49OFMkgEs20qY4ZAkB4dJsQ9pBZ2m+hxWE0hsy8WzDxuKV504c2GX3hnaamgi7j/OIvn5UxNSDoJrGwjrIpqgVENF+rnqpz+g3Nf8dBAkBV/op+6yMUGFBmbe1eCwAAD7XcC6f6DBrsU1lgi7n4Uw6JY75bkViEJqFmi+wJjI94uj7xRZRl6g8qx+rhfUvxAkEArD1we6ZLTWz73D8z+4Boj7Su7b5LhfgvXVL5V4Zdog3X58pHK+dYppapDh5KjZZdor+y/6uz1PqIs2dmFy/xtQ==";
private static String publicKeyString = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kdVkalQbr3Zl5oK3GeFTGOISBU2LetzRhDfWrZYJ54DZYjGrL8waH540bZOZrpHs1Cro7Tjepj5wIyiaBA3YmsN0ZNISr4FuTLM8lAROWy04XsVS24vp0GEn9hkr3dH43WZ+io4YFTnW2HviXblP4jw9/04uafsNzHsIGactkQIDAQAB";
private static PrivateKey privateKey;
private static PublicKey publicKey;
public void generateKeys() throws NoSuchAlgorithmException {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(1024);
KeyPair pair = keyGen.generateKeyPair();
RSAKeyPairGenerator.privateKey = pair.getPrivate();
RSAKeyPairGenerator.publicKey = pair.getPublic();
}
public void readKeys() throws NoSuchAlgorithmException, InvalidKeySpecException {
KeyFactory kf = KeyFactory.getInstance("RSA");
byte [] encoded = Base64.getDecoder().decode(privateKeyString);
PKCS8EncodedKeySpec keySpec1 = new PKCS8EncodedKeySpec(encoded);
RSAKeyPairGenerator.privateKey = kf.generatePrivate(keySpec1);
encoded = Base64.getDecoder().decode(publicKeyString);
X509EncodedKeySpec keySpec2 = new X509EncodedKeySpec(encoded);
RSAKeyPairGenerator.publicKey = kf.generatePublic(keySpec2);
}
public static void main(String[] args) throws NoSuchAlgorithmException, IOException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException {
RSAKeyPairGenerator keyPairGenerator = new RSAKeyPairGenerator();
// Next line is for testing with encrypted text generated by nodejs code with genrated keys
// keyPairGenerator.generateKeys();
keyPairGenerator.readKeys();
System.out.println("-----Private----------");
System.out.println(Base64.getEncoder().encodeToString(privateKey.getEncoded()));
System.out.println("Encoding : "+ privateKey.getFormat());
System.out.println("----------------------");
System.out.println("-----Public----------");
System.out.println(Base64.getEncoder().encodeToString(publicKey.getEncoded()));
System.out.println("Encoding : "+ publicKey.getFormat());
System.out.println("----------------------");
String secretMessage = "Test";
System.out.println(secretMessage);
Cipher encryptCipher = Cipher.getInstance("RSA");
encryptCipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] secretMessageBytes = secretMessage.getBytes(StandardCharsets.UTF_8);
byte[] encryptedMessageBytes = encryptCipher.doFinal(secretMessageBytes);
// Next lines are for testing decryption with encrypted text generated by nodejs code with generated keys
// byte[] encryptedMessageBytes = "B��u���<⌂�lǚm0�W������1�%EN�7��‼��l��l�<�����k;�������GĦ9D8��Z��I�Oɺ♫��→P'�§�{k�Ɋ+-}?��rZ".getBytes();
// byte[] encryptedMessageBytes = Base64.getDecoder().decode("woboAUytDXJLlKm7zbqNdxVORG+kio9kZxvMPOHruQfxwNEx7SVFTsw3oeETqbRs4NZskjzO2Nzjyms73vv758Dcy0fEpjlEOKmrWuG62knOT8m6DqGDGlAn1hXpe2u6yYorLX0/6fhyWg1C/JR1sbaKPH/Xt+Fsx5ptMONX0uw=");
System.out.println(new String(encryptedMessageBytes));
Cipher decryptCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA1AndMGF1Padding");
decryptCipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decryptedMessageBytes = decryptCipher.doFinal(encryptedMessageBytes);
String decryptedMessage = new String(decryptedMessageBytes, StandardCharsets.UTF_8);
System.out.println(decryptedMessage);
}
}
This gives me following output
-----Private----------
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMv+t+D6/7ZY6m+CjD97NB989P2YjmGcE2yHUokyjmxVi/SxIu9MboiVNewR+mFjTFYPGgMWHz1/XeVGlinjN/5+0fdiO/JEvEUilFmkkIF3My11I09b9NZ9RJmpCNCbqOg7NSt6VD8IC/w19N25xwcofc3qbgfEjKSs0CgxfahrAgMBAAECgYEAiyLlEBKiryDeZchJGFNULdXw07dmBbWKmg+CgAl3kvSWTQM0rLsY+Rese6OXfy1XN6t9NnW0QSHKTUNj0JYl7btKEblVHERbjwavUe83tcNIc3o0xrGoBAzma14ZicPYm70JWixTO778lm5AXKl504+oOQyVYmfgXcevPXwdIkECQQDvO3DBuu6wp5nbwR4t0736HOH9jVIjJOS8oABUARDhNt1fkb8uEptDr5EomVRMqarJMAjSMpYxrUCpvgtf4nZ5AkEA2ksDr8pbbFvZPaAe0F43LmJtFM3PvCz87S93p20YjwVMmGV17sJ5t7a26HtubgRLEWq4z6rxq2oXPv4y1lstAwJAbj9cVUtKWIrEcutqdwAPmsXYt7p60ctcxjiOLihXmRJprnNCQX89olG0eZs/qBzAofrK9eNuJ/KJzC/SmhuJMQJAAz9gc6oQCCGprrgGHVV5frAqLUgOkh8dOC4fmpcN6XrLs+y2f3HXO7t1JypG704TC9RJoZVKeSFf7Sj8+qFqnwJBALRiQc9SG5Ht5TLo12W4l3bRaxpbo597BgKRjz3hqiySdqjZA9Qe84qmEKveZ9eWehbfqwzKmCbW43I1ZEFZ95o=
Encoding : PKCS#8
----------------------
-----Public----------
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDL/rfg+v+2WOpvgow/ezQffPT9mI5hnBNsh1KJMo5sVYv0sSLvTG6IlTXsEfphY0xWDxoDFh89f13lRpYp4zf+ftH3YjvyRLxFIpRZpJCBdzMtdSNPW/TWfUSZqQjQm6joOzUrelQ/CAv8NfTduccHKH3N6m4HxIykrNAoMX2oawIDAQAB
Encoding : X.509
----------------------
Test
�N::�`��\A���ƈ�~��5s���
�0�I�C�uƹx2�Z&Kں�"ьC�$
q��K���h�^<�5�E�Ɨ0B�͒Z�{��EDbDH�.��#:�e��h~j���������q�c��R�
Test
In the Nodejs side I am using the above public to encrypt my text.
index.js
const crypto = require("crypto");
var pubkey = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDL/rfg+v+2WOpvgow/ezQffPT9mI5hnBNsh1KJMo5sVYv0sSLvTG6IlTXsEfphY0xWDxoDFh89f13lRpYp4zf+ftH3YjvyRLxFIpRZpJCBdzMtdSNPW/TWfUSZqQjQm6joOzUrelQ/CAv8NfTduccHKH3N6m4HxIykrNAoMX2oawIDAQAB\n-----END PUBLIC KEY-----";
var plaintext = "Test"
var buffer = new Buffer.from(plaintext, "utf8");
var enc = crypto.publicEncrypt(pubkey, buffer);
var bs64 = enc.toString('base64');
console.log(enc.toString());
console.log(enc.toString('base64'));
this gives me following output
B��u���<⌂�lǚm0�W������1�%EN�7��‼��l��l�<�����k;�������GĦ9D8��Z��I�Oɺ♫��→P'�§�{k�Ɋ+-}?��rZ
woboAUytDXJLlKm7zbqNdxVORG+kio9kZxvMPOHruQfxwNEx7SVFTsw3oeETqbRs4NZskjzO2Nzjyms73vv758Dcy0fEpjlEOKmrWuG62knOT8m6DqGDGlAn1hXpe2u6yYorLX0/6fhyWg1C/JR1sbaKPH/Xt+Fsx5ptMONX0uw=
Now I tried using both the strings in java side for decryption.
When I try decoding the string, it gives me following error
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Data must not be longer than 128 bytes
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:346)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:391)
at javax.crypto.Cipher.doFinal(Cipher.java:2168)
at com.mindtree.starr.wsr.RSAKeyPairGenerator.main(RSAKeyPairGenerator.java:90)
And, when I try decoding the base64 string, it gie me following error
Exception in thread "main" javax.crypto.BadPaddingException: Decryption error
at sun.security.rsa.RSAPadding.unpadV15(RSAPadding.java:379)
at sun.security.rsa.RSAPadding.unpad(RSAPadding.java:290)
at com.sun.crypto.provider.RSACipher.doFinal(RSACipher.java:365)
at com.sun.crypto.provider.RSACipher.engineDoFinal(RSACipher.java:391)
at javax.crypto.Cipher.doFinal(Cipher.java:2168)
at com.mindtree.starr.wsr.RSAKeyPairGenerator.main(RSAKeyPairGenerator.java:90)
What am I doing wrong?
The problem was clearly described in the first comment and the solution is in second comment.
Correct transformation string for Cipher instance that worked for me is RSA/ECB/OAEPWithSHA-1AndMGF1Padding
So below instantiation for decryption Cipher works perfectly.
Cipher decryptCipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
I have an actual public key string like:
-----BEGIN PUBLIC KEY-----
flajeleofancncMFLDFJOEEFJC9209ueq33rlsjfa3B ...
-----END PUBLIC KEY-----
In order to create an auth0/java-jwt-library Algorithm to sign my JWT, I need a java.security.interfaces.RSAPublicKey-implementation instance. How would I go about creating that instance given public key string? If it helps, I also have the private key string.
I'm just starting out. So, I'm open to simpler ways to signing my JWT.
import java.security.KeyFactory;
import java.security.spec.X509EncodedKeySpec;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.EncodedKeySpec;
import java.security.interfaces.RSAPrivateKey;
...
String algorithm = "RSA" // for example
KeyFactory kf = KeyFactory.getInstance(algorithm);
String publicKeyStr = "-----BEGIN PUBLIC KEY-----f24Defosfvak-----END PUBLIC KEY-----"
EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyStr.getBytes());
RSAPublicKey publicKey = kf.generatePublic(keySpec);
I'm trying to generate my own CSR for my keystore, but it didn't go well and that error is confusing me. Here is my code:
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.X500NameBuilder;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.openssl.PEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
public class CreateKeyTest {
public static void main(String[] args) throws OperatorCreationException, IOException, GeneralSecurityException {
KeyPairGenerator kpg;
KeyPair kp;
RSAPublicKey pubKey;
RSAPrivateKey privKey;
FileOutputStream out;
KeyStore ks;
FileInputStream in;
FileInputStream bFis;
try {
ks = KeyStore.getInstance("JKS");
kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(1024);
kp = kpg.generateKeyPair();
pubKey = (RSAPublicKey) kp.getPublic();
privKey = (RSAPrivateKey) kp.getPrivate();
// generate CSR
ContentSigner sign = new JcaContentSignerBuilder("SHA1withRSA").build(privKey);
X500NameBuilder nBuilder = new X500NameBuilder();
nBuilder.addRDN(BCStyle.CN, "TestCSR");
nBuilder.addRDN(BCStyle.C, "ER");
nBuilder.addRDN(BCStyle.E, "test#test.com");
X500Name name = nBuilder.build();
PKCS10CertificationRequestBuilder cerReq = new JcaPKCS10CertificationRequestBuilder(name, pubKey);
PKCS10CertificationRequest request = cerReq.build(sign);
PEMWriter pWr = new PEMWriter(new FileWriter(new File("D:\\test.csr")));
pWr.writeObject(request);
pWr.flush();
pWr.close();
bFis = new FileInputStream("D:\\test.csr");
BufferedInputStream ksbufin = new BufferedInputStream(bFis);
X509Certificate certificate = (X509Certificate) CertificateFactory.getInstance("X.509")
.generateCertificate(ksbufin);
ks.setKeyEntry("RSA_key", kp.getPrivate(), "changeit".toCharArray(),
new java.security.cert.Certificate[] { certificate });
out = new FileOutputStream("key.store");
ks.store(out, "changeit".toCharArray());
System.out.println("New Keystore Generated");
out.close();
} catch (KeyStoreException | IOException | CertificateException | NoSuchAlgorithmException
| OperatorCreationException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
When I execute it, it showed me the exception:X509.ObjectIdentifier() -- data isn't an object ID (tag = 49), and it could be back-traced to generateCertificate(ksbufin). But I checked test.cer and it do have certificate data in there, and that exception message confused me, don't even know what does that mean(object ID? tag = 49? I didn't see I generated an ID in my code.).
Can anyone help me out this mud?
The error message is correct, test.csr does not contain a certificate. You have built it using a PKCS10CertificationRequest, so it consenquently contains a Certificate Signing Request (CSR).
You have generated a key pair, private and public, and a CSR. The CSR is a request of a certificate to a Certification Authority (CA). It contains the public key and some expected attributes for the certificate (CN, C, OU, etc). CSR is signed with the private key and has to be sent to CA. The CA will extract the public key, generates a certificate and signs it. See Certificate enrollment process
If you want a Certificate, you need to get signed the certificate by the CA
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.