Extracting Private key from pkcs12 and text encryption - java

I have .p12 file, I am extracting the private key using openssl, I have a password for extracting it.
openssl pkcs12 -in my.p12 -nocerts -out privateKey.pem
And after I get my private key, I'm trying to use that key for encryption:
public static void main(String[] args) throws Exception {
Security.addProvider(new BouncyCastleProvider());
KeyPair keyPair = readKeyPair(privateKey, "testpassword".toCharArray());
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] textEncrypted = cipher.doFinal("hello world".getBytes());
System.out.println("encrypted: "+new String(textEncrypted));
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
byte[] textDecrypted = cipher.doFinal(textEncrypted);
System.out.println("decrypted: "+new String(textDecrypted));
}
private static KeyPair readKeyPair(File privateKey, char[] keyPassword) throws IOException {
FileReader fileReader = new FileReader(privateKey);
PEMReader r = new PEMReader(fileReader, new DefaultPasswordFinder(keyPassword));
try {
return (KeyPair) r.readObject(); // this returns null
} catch (IOException ex) {
throw new IOException("The private key could not be decrypted", ex);
} finally {
r.close();
fileReader.close();
}
}
r.readObject(); returns null. But when I create a private key by myself by this command:
openssl genrsa -out privkey.pem 2048
The above code works fine.
How can I extract private key from p12 file properly?
Or is there any way to use p12 file for encrypt/decrypt the text
without extracting through command line?
I know it is just PKCS#12 is just archaive file which stores keys.

I don't know what is wrong with your code, but I have code that reads stuff from a key store. I read the file into a KeyStore instance and then access the key or entry as appropriate. Here are some of the relevant calls:
char[] password;
String alias;
java.security.KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
keyStore.load(inputStream, password);
java.security.PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, password);
java.security.keystore.PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry(alias, new KeyStore.PasswordProtection(password));
To find the alias of the entry you are interested in, I suggest using keytool (comes with JDK):
keytool -list -v -keystore keystore.pkcs12 -storetype pkcs12
You will be prompted for the keystore password and then get information like this:
Keystore type: PKCS12
Keystore provider: SunJSSE
Your keystore contains 1 entry
Alias name: thealias
Creation date: Aug 30, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 2
[... lots of info about the certificates deleted ...]

Related

java - How to generate PrivateKey and PublicKey starting from a keystore (.p12)

Generate some keys with OpenSSL, then encode them in Base64 and obtain them and try to generate them to validate the authentication with JWT. Here is the code and description of what happens to me
Generate with the following commands:
openssl req -x509 -newkey rsa:4096 -keyout private_key.pem -out public_key.der
openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der
base64 –w 0 private_key.pem > private_key_base64_enc.txt
base64 –w 0 public_key.der > public_key_base64_enc.txt
I saved in my vault.keystore from wildfly: private_key_base64_enc.txt and public_key_base64_enc.txt
Then in my java class I write the following:
private void jwtSignedAuthentication(String token, PropName vaultBlockName) throws Exception
{
String rsa512Alias = vaultBlockName.getDefaultValue();
String rsa512pvt = VaultReader.getValue(rsa512Alias, "privateKey");
String rsa512pbc = VaultReader.getValue(rsa512Alias, "publicKey");
KeyFactory keyfatc = null;
PrivateKey privateKey = null;
PublicKey publicKey = null;
try {
keyfatc = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
logger.error(e);
}
StringBuilder pkcs8Lines = new StringBuilder();
BufferedReader rdr = new BufferedReader(new StringReader(new String(Base64.getDecoder().decode(rsa512pvt.getBytes()))));
String line;
while ((line = rdr.readLine()) != null) {
pkcs8Lines.append(line);
}
// Remove the "BEGIN" and "END" lines, as well as any whitespace
String pkcs8Pem = pkcs8Lines.toString();
pkcs8Pem = pkcs8Pem.replace("-----BEGIN ENCRYPTED PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replace("-----END ENCRYPTED PRIVATE KEY-----", "");
pkcs8Pem = pkcs8Pem.replaceAll("\\s+","");
byte[] dataPvt = Base64.getDecoder().decode(pkcs8Pem.getBytes());
PKCS8EncodedKeySpec specPvt = new PKCS8EncodedKeySpec(dataPvt);
byte[] dataPbc = Base64.getDecoder().decode(rsa512pbc.getBytes());
StringBuilder publicLinesBuilder = new StringBuilder();
BufferedReader readerPlubKey = new BufferedReader(new StringReader(new String(dataPbc)));
String lineP;
while ((lineP = readerPlubKey.readLine()) != null) {
publicLinesBuilder.append(lineP);
}
String pubK = publicLinesBuilder.toString();
pubK = pubK.replace("-----BEGIN CERTIFICATE-----", "");
pubK = pubK.replace("-----END CERTIFICATE-----", "");
pubK = pubK.replaceAll("\\s+","");
X509EncodedKeySpec specPbc = new X509EncodedKeySpec(Base64.getDecoder().decode(pubK.getBytes()));
try {
privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);
} catch (InvalidKeySpecException e) {
logger.error(e);
}
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) publicKey, (RSAPrivateKey) privateKey);
// Creación de un verificador JWT
JWTVerifier verifier = JWT.require(algorithm).withIssuer(JWT_CLAIM_ISSUER).acceptLeeway(2).build();
UserContext userContext = new UserContext();
userContext.setUserName(JWT_CLAIM_ISSUER);
try {
// Decode JWT, verificación del token.
#SuppressWarnings("unused")
DecodedJWT decodeJwt = verifier.verify(token);
} catch (JWTDecodeException e) {
logger.error(e);
}
}
When I try to generate the keys I return null:
privateKey = keyfatc.generatePrivate(specPvt);
publicKey = keyfatc.generatePublic(specPbc);
Anyone have any idea what happens with this. Thanks in advance
For generate my JWT:
public ResteasyWebTarget getClientWebAgent(String host, String blockName) throws KeyStoreException
{
ResteasyClient clientBuilder = new ResteasyClientBuilder().establishConnectionTimeout(10, TimeUnit.SECONDS).socketTimeout(5, TimeUnit.SECONDS).build();
ResteasyWebTarget target = clientBuilder.target(host);
KeyPair keys = null;
try {
keys = keyStore.getKeys();
/*logger.infov(new String(Base64.getEncoder().encode(keys.getPrivate().getEncoded())));
logger.infov("****PUBLIC KEY ******");
logger.infov(new String(keys.getPublic().getEncoded()));*/
} catch (IOException e) {
logger.error(e);
}
Algorithm algorithm = Algorithm.RSA512((RSAPublicKey) keys.getPublic(), (RSAPrivateKey) keys.getPrivate());
Map<String, Object> headerClaims = new HashMap<>();
headerClaims.put("alg", "RS512");
headerClaims.put("typ", "JWT");
JWTCreator.Builder jwtCreator = JWT.create();
jwtCreator.withHeader(headerClaims);
jwtCreator.withIssuer(JWT_CLAIM_ISSUER);
jwtCreator.withIssuedAt(LocalDate.now().toDate());
jwtCreator.withExpiresAt(LocalDate.now().toDateTimeAtCurrentTime().plusSeconds(30).toDate());
String jwtToken = jwtCreator.sign(algorithm);
target.register(new BearerAuthenticator(jwtToken));
target.register(new LanguageHeaderToken(Locale.getDefault()));
return target;
}
Your 'public key' is actually a certificate (specifically an X.509 v1 or v3 certificate, depending on your openssl config), which contains a publickey but is different from a publickey -- and is in PEM format even though you have misleadingly named it .der -- and your privatekey is encrypted.
In addition to the approach of using a PKCS12, as Roberto validly proposes and is usually the simplest because it's only one file to manage and is still encrypted and thus more secure:
Java can handle an X.509 certificate, but you use a CertificateFactory.getInstance("X.509") and give it an InputStream instead of a KeyFactory and an X509EncodedKeySpec. CertificateFactory can handle either PEM or DER, unlike KeyFactory which can handle only DER, so you don't need the de-PEM (strip BEGIN/END/EOL and decode base64) parts.
standard Java cannot handle encrypted PKCS8 keys directly. If you can add a thirdparty library, BouncyCastle's bcpkix can do so; search the dozen or so existing Qs that use PEMParser (not PEMReader, that's the older version) and JceOpenSSLPKCS8DecryptorBuilder. Otherwise, you can add -nodes to your req -newkey -x509 command to generate an unencrypted privatekey file, which after you de-PEM it does work in KeyFactory with PKCS8EncodedKeySpec. (It's still spelled -nodes even though the encryption used without it hasn't been plain aka single DES for decades.) Using an unencrypted privatekey file of course means that any intruder or malware on your system that can read that file can get your privatekey, which in many situations is a risk.
finally, if you really want only the keypair and not a certificate, don't bother with req -newkey -x509. Instead use openssl genpkey to generate the privatekey, or the older but simpler openssl genrsa -nodes followed by (or piped to) openssl pkcs8 -topk8 -nocrypt to convert it to PKCS8-unencrypted format. Then use openssl pkey -pubout or the older openssl rsa -pubout to make a separate file with the publickey. Those commands can write (and read back where applicable) DER format instead of PEM; if you do that, your code doesn't need the de-PEM steps, you can just pass the binary file contents to KeyFactory. The risks for an unencrypted file are the same as above.
Maybe you are generating the keystore without assigning a valid alias, looking at your command you are not using the -name option.
The command should be like this:
openssl pkcs12 -export -out keyStore.p12 -inkey private_key.pem -in public_key.der -name "alias"
A smarter way to use the keys in java is by creating a KeyPair:
KeyPair loadKeyPair() throws Exception {
// Read keystore from resource folder
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL resource = classLoader.getResource("keyStore.p12");
File file = new File(Objects.requireNonNull(resource).toURI());
char[] keyPass = "1234".toCharArray();
String alias = "alias";
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
try (FileInputStream is = new FileInputStream(file)) {
keystore.load(is, keyPass);
}
Key key = keystore.getKey(alias, keyPass);
if (key instanceof PrivateKey) {
// Get certificate of public key
Certificate cert = keystore.getCertificate(alias);
// Get public key
PublicKey publicKey = cert.getPublicKey();
// Return a key pair
return new KeyPair(publicKey, (PrivateKey) key);
}
return null;
}
Then extract RSAPublicKey and RSAPrivateKey keys from the KeyPair:
void loadKeys() throws Exception{
KeyPair keyPair = loadKeyPair();
if (null != keyPair) {
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
RSAPrivateKey rsaPrivateKey = (RSAPrivateKey) keyPair.getPrivate();
}
}
Hope it can be helpful and good luck with your Json Web Tokens! :-p

How to programmatically generate and store HMacSHA256 key in Java?

I would like to generate and store a HMacSHA256 key for testing purposes in the Java keystore.
I would normally do this via the keytool:
keytool -genseckey -keystore keystore.jceks -storetype jceks -storepass secret -keyalg HMacSHA256 -keysize 2048 -alias HS256 -keypass secret
So far I found that I can generate the key using:
SecretKey key = new SecretKeySpec("secret".getBytes(), "HmacSHA256");
That key is unfortunately not an instance of PrivateKey and thus storing the key fails:
KeyStore ks = ...
ks.setEntry("HS256", new SecretKeyEntry(key), new PasswordProtection("secret".toCharArray()));
Exception:
java.security.KeyStoreException: Cannot store non-PrivateKeys
at sun.security.provider.JavaKeyStore.engineSetKeyEntry(JavaKeyStore.java:258)
at sun.security.provider.JavaKeyStore$JKS.engineSetKeyEntry(JavaKeyStore.java:56)
at java.security.KeyStoreSpi.engineSetEntry(KeyStoreSpi.java:550)
at sun.security.provider.KeyStoreDelegator.engineSetEntry(KeyStoreDelegator.java:179)
at sun.security.provider.JavaKeyStore$DualFormatJKS.engineSetEntry(JavaKeyStore.java:70)
at java.security.KeyStore.setEntry(KeyStore.java:1557)
at com.gentics.mesh.SecretKeyTest.testSHA(SecretKeyTest.java:31)
I believe that SecretKey represents a symmetric key. And PrivateKey is part of a PublicKey and Private-Key Pair. Is there a way to store a single symmetric key?
Yes, you can. But until Java 9 comes out, PKCS#12 key stores will be limited in functionality. JCEKS key stores as you are using in the command line keytool do however support symmetric (HMAC) keys:
public class HMACKeyStore {
public static void gen( String thePath, String thePassword ) throws Exception {
KeyGenerator keygen = KeyGenerator.getInstance("HmacSHA256");
SecretKey key = keygen.generateKey();
KeyStore keystore = KeyStore.getInstance("jceks");
keystore.load(null, null);
// This call throws an exception
keystore.setKeyEntry("theKey", key, thePassword.toCharArray(), null);
keystore.store( new FileOutputStream(thePath), thePassword.toCharArray() );
SecretKey keyRetrieved = (SecretKey) keystore.getKey("theKey", thePassword.toCharArray());
System.out.println(keyRetrieved.getAlgorithm());
}
public static void main(String[] args) throws Exception {
gen("hmac_store.jceks", "password");
}
}
should work fine on Java 8.

How to convert x509 Cert and Key to a pkcs12 file

To convert a pem file containing a x509 certificate + private key into a pkcs12 (.p12) file, the following command is being used:
openssl pkcs12 -export -inkey cert_pkey.pem -in cert_pkey.pem -out cert.p12
I am trying to accomplish the same programatically using Java with BouncyCastle library. I am able to extract the X509Cert from the PEMObject but the Private key has been confusing.
Any help in piecing together the steps is appreciated:
Open cert_pkey.pem file stream using PEMParser
Get the X509 Certificate from PemObject (done)
Get the private key from the PemObject (how?)
Create KeyStore of instance type PKCS12 with password
Finally got around how to get the cert and key separately - not sure why it worked out the way it worked out:
PEMParser pemParser = new PEMParser(new BufferedReader(new InputStreamReader(certStream)));
Object pemCertObj = pemParser.readObject();
PemObject pemKeyObj = pemParser.readPemObject();
PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(pemKeyObj.getContent());
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privKey = kf.generatePrivate(privKeySpec);
Security.addProvider(new BouncyCastleProvider());
X509CertificateHolder certHolder = (X509CertificateHolder)pemCertObj;
X509Certificate x509cert = (new JcaX509CertificateConverter()).setProvider("BC").getCertificate(certHolder);
I got the hint when I looked up the .getType() on permCertObj and permKeyObj and got RSA CERT and RSA PRIVATE KEY respectively returned.
Couldn't figure out the difference between readObject() and readPemObject()
The PEMParser class will parse just about anything from PEM format. You can read the object from the file using that parser - if you'll print the class of that object you'l;l see it's a PEMKeyPair. That can be converted to a regular KeyPair using JcaPEMKeyConverter.
public KeyPair importKeyFromPemFile(String filePath)
{
try (FileReader reader = new FileReader(filePath))
{
PEMParser pemParser = new PEMParser(reader);
PEMKeyPair pemKeyPair = (PEMKeyPair)pemParser.readObject()
return new JcaPEMKeyConverter().getKeyPair(pemKeyPair);
}
catch (IOException | PEMException e)
{
throw new RuntimeException(e)
}
}

Issue in generating private key

Private key generation
public PrivateKey getStoredPrivateKey(String filePath) {
PrivateKey privateKey = null;
byte[] keydata = getKeyData(filePath);
PKCS8EncodedKeySpec encodedPrivateKey = new PKCS8EncodedKeySpec(keydata);
KeyFactory keyFactory = null;
try {
keyFactory = KeyFactory.getInstance("RSA");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
try {
System.out.println("hello");
privateKey = keyFactory.generatePrivate(encodedPrivateKey);
} catch (InvalidKeySpecException e) {
e.printStackTrace();
}
return privateKey;
}
I am using it here
PrivateKey privateKey = new KryptoUtil().getStoredPrivateKey(privateKeyFilePath);
but its showing error
hello
java.security.spec.InvalidKeySpecException:
java.security.InvalidKeyException: IOException : version mismatch: (supported: 00, parsed: 03
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(Unknown Source)
I am passing a (.p12) file in getStoredPrivateKey(String filePath) function.
why its giving error?
P12 is is keystore type where multiple keys and certificates can be stored and a password can be used to protect them. You can search about P12 (PKCS12) on Internet. Your file is P12 file, so most likely it is PKCS12 format file.
To get private key from P12 file use below code. You need below things before calling this code.
filePath. String path (absolute) of P12 file.
filePassword. It is a char[]. Represents password of p12 file.
keyPassword. It is a char[]. Represents password for private key. Most
likely it is same as filePassword.
alias. A String. Represents by which alias a private key stored in P12
archive/keystore.
To check what is the alias of your private key you can use below command
keytool -list -v -keystore <yourfile>.p12 -storetype pkcs12
It will ask for password then print multiple lines. Look for
Entry Type: PrivatKeyEntry
There you will find the alias.
Initialize these variables and then use below code to get private key. You can also get Certificates/Public key associate with this key. Look for API of PrivateKeyEntry
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(new FileInputStream(filePath), filePassword);
PrivateKeyEntry keyEntry = (PrivateKeyEntry) ks.getEntry(alias, new KeyStore.PasswordProtection(keyPassword));
PrivateKey key = privateKeyEntry.getPrivateKey();

How to verify signatureBytes after signing it with SHA256withRSA?

I am Signing some text using "Windows-MY" KeyStore .
I want to sign using my private key and verify using Public Key.
KeyStore keyStore = KeyStore.getInstance("Windows-MY");
keyStore.load(null, null);
Enumeration en = keyStore.aliases();
while (en.hasMoreElements()) {
KeyStore keyStore = KeyStore.getInstance("Windows-MY");
keyStore.load(null, null);
String alias = en.nextElement().toString();
X509Certificate c = (X509Certificate) keyStore.getCertificate(alias);
String serialNumber = c.getSerialNumber().toString();
System.out.println("--" + aliasName);
PrivateKey privateKey = (PrivateKey) keyStore.getKey(aliasName, null);
PublicKey publicKey = (PublicKey) c.getPublicKey();
Certificate[] chain = keyStore.getCertificateChain(aliasName);
DataOutputStream fout = new DataOutputStream(outstream);
// -------------------------------------------------------
String data = "Monika";
byte[] content = data.getBytes();
Provider p = keyStore.getProvider();
// ----------------------signature---start---------------------------
Signature signature = Signature.getInstance("SHA256withRSA", p);
System.out.println(" signature.getProvider():"+ signature.getProvider());
signature.initSign(privateKey);
signature.update(content);
byte[] signatureBytes = signature.sign();
System.out.println("signatureBytes-------------"+ signatureBytes.toString());
// ----------------------signature----------end------------------
// ------------------------verification---------------
Signature signature1 = Signature.getInstance("SHA256withRSA", p);
System.out.println(" signature1.getProvider():"+ signature1.getProvider());
signature1.initVerify(publicKey);
signature1.update(content);
boolean verifies = signature1.verify(signatureBytes);
System.out.println("signature verifies: " + verifies);
// ------------------------------------------------
fout.close();
} // while
Output:
privateKey:RSAPrivateKey [size=2048 bits, type=Exchange, container=AC0BEBA9-A361-4611-96D9-B365B671FBC3]
signature.getProvider():SunMSCAPI version 1.6
signatureBytes-------------[B#1402d5a
signature1.getProvider():SunRsaSign version 1.5
signature verifies: false
Notice that:
My Private key is already RSAPrivateKey .
Provider for Signing is SunMSCAPI.
But I dont know about Provider for Verification with PrivateKey.
There are several issues with your code:
You are simply using the first certificate / public key from your windows keystore. This might actually be the right one here, but there might be more than one certificate in the keystore and then it is just coincidence which certificate you are using for verify.
String alias = en.nextElement().toString();
X509Certificate c = (X509Certificate) keyStore.getCertificate(alias);
PublicKey publicKey = c.getPublicKey();
PrivateKey privateKey = (PrivateKey) keyStore.getKey(DSCName, null);
You should write keyStore.getCertificate(DSCName) instead to make sure it matches the private key.
You are generating a key (resp. trying to convert the existing key) for no reason. You can remove this code completely. This will also solve your problem with the NullPointerException:
byte[] encodedPrivateKey = privateKey.getEncoded();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
RSAPrivateKey privateKey1 = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
There is a lot of unnecessary code in your question, like loading the certificate chain, but never using it. That makes it harder to fix. A minimal (working) example would look like this:
String alias = "myAlias";
String myData = "data to encrypt";
KeyStore keyStore = KeyStore.getInstance("Windows-MY");
keyStore.load(null, null);
X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
PrivateKey privateKey = (PrivateKey) keyStore.getKey(alias, null);
PublicKey publicKey = cert.getPublicKey();
Signature instance = Signature.getInstance("SHA256withRSA");
instance.initSign(privateKey, new SecureRandom());
instance.update(myData.getBytes("UTF-8"));
byte[] signedBytes = instance.sign();
instance.initVerify(publicKey);
instance.update(myData.getBytes("UTF-8"));
System.out.println(instance.verify(signedBytes));
Signature signature = Signature.getInstance("SHA256withRSA",p);
System.out.println(" signature.getProvider():"+ signature.getProvider());
signature.initSign(privateKey, new SecureRandom());
signature.update(byteData);
byte[] signatureBytes = signature.sign();
// X509Certificate cert1 =signatureBytes.
System.out.println("signatureBytes-------------"+ signatureBytes.toString());
// ----------------------signature----------end------------------
// ------------------------verification---------------
Signature signature1 = Signature.getInstance("SHA256withRSA");
System.out.println(" signature1.getProvider():"+ signature1.getProvider());
signature1.initVerify(publicKey);
signature1.update(byteData);
boolean verifies = signature1.verify(signatureBytes);
System.out.println("signature verifies: " + verifies);
You are getting null here
byte[] encodedPrivateKey = privateKey.getEncoded(); // are you sure that this byte array is not null ?
To make things more safe check here:
PrivateKey privateKey = (PrivateKey) keyStore.getKey(
DSCName, null); // this maybe returning null
so before the line which gives error, make a check:
if(encodedPrivateKey==null){
System.out.println("private key is null");
}
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(
encodedPrivateKey);
Check this complete and working code to verify signature after signing some text/data using digest SHA256withRSA:
/* verifyRSAsha256.java */
import java.security.GeneralSecurityException;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;
import javax.xml.bind.DatatypeConverter;
/*
Compile:
clear && javac verifyRSAsha256.java && java verifyRSAsha256
Create private key:
openssl genrsa -des3 -out encrypted.pem 2048 && openssl rsa -in encrypted.pem -out private.pem -outform PEM && openssl rsa -in private.pem -pubout > public.pem
Create signature:
/bin/echo -n "some text that you want to be trusted" > data.txt
openssl dgst -sha256 -sign private.pem data.txt > signature.tmp
base64 signature.tmp
Verify signature:
openssl dgst -sha256 -verify public.pem -signature signature.tmp data.txt
*/
public class verifyRSAsha256 {
public static void main(String args[]){
String publicKey =
// "-----BEGIN PUBLIC KEY-----"+
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwns0JWYgEshlpLYsZQFc"+
"d5iVSqIHDO0zISLlO1aK4bbbosSvRE81+inKrG5mlnkIrv0+mJ/qTLY1RdBAVAe4"+
"GPLTpHmLJhEtq7stydm2cCTEPRwfJNjoHqATDHEm1KLVGA8k0hztfMr8fLChE3/K"+
"n2MHxzs7qhMLyBdPqbVC9RNja3i+Nl814xPTSXJ50zdJMLC56VtIU0xjqNjXN8iQ"+
"pLZ2EfcP55nZ/venD01yxfsUn4sQLFTAlXqygA10fdDv9y0eZvgaGGSb4MuPT7yD"+
"BfgNEU3tl4nRdSzPNkCkCmkuaa/pqZ5uw+G0HBwaQlHDwsnIcwE/xo6aHpt4xF4W"+
"/QIDAQAB";
// "-----END PUBLIC KEY-----";
byte[] signature, data;
// the signature is a binary data and I encoded it with base64, so the signature must be decoded from base64 to binary again
signature = DatatypeConverter.parseBase64Binary("Yy9CdQDfdYWwZkSu2SZgoFABHk5Bd3tzYvX73QR+GDCWpUsWrO5CXRF+j3dBz+bq1SRQ+1c1hdez5mMeE1587s4Mos8EsT1sqNemu4l4535P+jYicwG1m2MAesquAHhIIAyY9iGID576ehX0/34rCCeGuVZablpL+2ki6cEwxPVlH7xtWNIc1AdxivHjkWorkWC1LrbfcNdbZhUrNuz7DZsxHP2sr+2TQdD4L9CA2bgpj6HeQt+MTfCf2PKSdVoLFdwnM8638jHL6MVcEJxeIow/YUDZGEAyR743RdRk4CGU1bJ7er9M1Q4hFfYWGOBsLBok2XXUJcchLgWET1eKdA==");
// the signature length have to be 256 bytes
System.out.print("Signature length 256 = ");
System.out.println(signature.length);
// the data used the generate the signature
data = "some text that you want to be trusted".getBytes();
// verify if signature is ok
try {System.out.println(verify(data,signature,publicKey));}catch(GeneralSecurityException e){e.printStackTrace();}
// if any byte of data changes (ex: change last byte from d to D)
data = "some text that you want to be trusteD".getBytes();
// the signature doesn't math and method verify will fail
try {System.out.println(verify(data,signature,publicKey));}catch(GeneralSecurityException e){e.printStackTrace();}
}
private static boolean verify(byte[] data, byte[] signature, String publicKey) throws GeneralSecurityException{
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(DatatypeConverter.parseBase64Binary(publicKey));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey pubKey = keyFactory.generatePublic(pubKeySpec);
Signature sig = Signature.getInstance("SHA256withRSA");
sig.initVerify(pubKey);
sig.update(data);
return sig.verify(signature);
}
}
You can create a private and public key using openssl command line tool:
openssl genrsa -des3 -out encrypted.pem 2048
openssl rsa -in encrypted.pem -out private.pem -outform PEM
openssl rsa -in private.pem -pubout > public.pem
You can create one signature with your private key using openssl command line tool:
/bin/echo -n "some text that you want to be trusted" > data.txt
openssl dgst -sha256 -sign private.pem data.txt > signature.tmp
You can verify if the signature is correct using openssl command line tool:
openssl dgst -sha256 -verify public.pem -signature signature.tmp data.txt

Categories

Resources