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.
Related
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 code sample in Java and I need the same functionality in C#. Are there any alternatives to the classes, which are used for the sample?
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import javax.xml.bind.DatatypeConverter;
publicKey = publicKey.replaceAll("-----(BEGIN|END).*", "").trim();
X509EncodedKeySpec spec = new X509EncodedKeySpec(DatatypeConverter.parseBase64Binary(publicKey));
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PublicKey pKey = keyFactory.generatePublic(spec);
Signature ecdsaSign = Signature.getInstance("SHA256withECDSA");
ecdsaSign.initVerify(pKey);
ecdsaSign.update(stringToVerify.getBytes("UTF-8"));
if (ecdsaSign.verify(new BigInteger(ECDSA, 16).toByteArray())) {
// true
}
There's not anything in .NET (by itself) which can read the public key structure only. But if you can get the entirety of a certificate then you can write the following in .NET 4.6.1:
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
namespace Demo
{
public static class DemoClass
{
public static bool ECDsaSha256Verify(string pemCert, string data, byte[] signature)
{
using (var cert = new X509Certificate2(Encoding.ASCII.GetBytes(pemCert)))
using (ECDsa ecdsa = cert.GetECDsaPublicKey())
{
if (ecdsa == null)
{
throw new ArgumentException("Certificate does not have an ECDSA key.");
}
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
return ecdsa.VerifyData(dataBytes, signature, HashAlgorithmName.SHA256);
}
}
}
}
I wasn't sure where your BigInteger signature value was coming from, so that I have just left as a byte array.
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'm trying to connect to a third party application API using apache commons HTTP Client. The API I'm trying to connect is http://wiki.kayako.com/display/DEV/REST+API.
The API requires me to pass a API key and a signature along with a salt used to create the signature.
As per the API documentation these are the steps to create the signature
Generate a random string to create a salt (in PHP, you would use mt_and() to do this)
Generate the signature by hashing the salt using SHA256 with the secret key as the key (in PHP, you would use hash_hmac() to do this)
base64 encode the signature (in PHP, you would use base64_encode() to do this)
URL encode the output (in PHP, you would use urlencode() to do this)
UPDATED
As per the responses I got, I changes some of my code and created a demo account with the Kayako to test the API
I'm using the following class to generate the signature
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.GeneralSecurityException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.util.encoders.Base64Encoder;
public class GenSign2 {
public static void main(String[] args) throws GeneralSecurityException,
IOException {
String secretKey = "M2Y2YjkxZDEtYmNlOC1mYmI0LTkxZTgtOTNiY2RiMDhmN2E2YjExNGUwYjktNGJkYy1jZTM0LWQ1MWYtZGIwYWRlZTE0NGNh";
String salt = "0123456789";
String generateHmacSHA256Signature = generateHmacSHA256Signature(salt,
secretKey);
System.out.println("Signature: " + generateHmacSHA256Signature);
String urlEncodedSign = URLEncoder.encode(generateHmacSHA256Signature,
"UTF-8");
System.out.println("Url encoded value: " + urlEncodedSign);
}
public static String generateHmacSHA256Signature(String data, String key)
throws GeneralSecurityException, IOException {
byte[] hmacData = null;
try {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"),
"HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secretKey);
hmacData = mac.doFinal(data.getBytes("UTF-8"));
ByteArrayOutputStream bout = new ByteArrayOutputStream();
new Base64Encoder().encode(hmacData, 0, hmacData.length, bout);
return bout.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new GeneralSecurityException(e);
}
}
}
And the test api is as follows
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
public class TestApi {
public static void main(String[] args) throws ClientProtocolException,
IOException, URISyntaxException {
HttpClient client = new DefaultHttpClient();
List<NameValuePair> qparams = new ArrayList<NameValuePair>();
qparams.add(new BasicNameValuePair("apikey",
"f165dc40-ce3f-6864-7d5e-27a7188b2e62"));
qparams.add(new BasicNameValuePair("salt", "0123456789"));
qparams.add(new BasicNameValuePair("signature", "mbrhpXkP0LzNMNDygHAorqMx%2FDGovl%2FauMTOMB6RNMA%3D"));
HttpPost httpget = new HttpPost(
"http://aruntest.kayako.com/api/index.php?e=/Core/Test");
HttpResponse response = client.execute(httpget);
System.out.println(response.getProtocolVersion());
System.out.println(response.getStatusLine().getStatusCode());
System.out.println(response.getStatusLine().getReasonPhrase());
System.out.println(response.getStatusLine().toString());
}
}
The demo site can be accessed using
URL: http://aruntest.kayako.com/admin/
User: admin
Password: ty386rhjzz
It is throwing an unauthorized access exception when I'm trying to connect.
Try and compare your signature method with this (it works)
public static String generateHmacSHA256Signature(String data, String key) throws GeneralSecurityException {
byte[] hmacData = null;
try {
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secretKey);
hmacData = mac.doFinal(data.getBytes("UTF-8"));
return new BASE64Encoder().encode(hmacData);
} catch (UnsupportedEncodingException e) {
// TODO: handle exception
throw new GeneralSecurityException(e);
}
}
The result of this call, will then be the value of your attribute Signature
String signature = generateHmacSHA256Signature(salt, key);
qparams.add(new BasicNameValuePair("signature", signature));
A simple way to generate a salt/nonce
String nonce = String.valueOf(System.currentTimeMillis());
See Example:
Kayako has updated their documentation with a new java sample which works fine.
I think the entire getSaltedKey() routine is unnecessary. You're just signing the salt (it should have been called a nonce) with HMAC and signing with the provided key, it doesn't look like you're supposed to sign the key+salt.