Java verify digital signature in openssl [duplicate] - java

This question already has an answer here:
Why are the RSA-SHA256 signatures I generate with OpenSSL and Java different?
(1 answer)
Closed 6 years ago.
Using this tutorial, I created a Java program what can sign a document with SHA256withRSA algorithm. In the output I get a public key and a signature file.
I try to verify my file with openssl, but I can't... I was searching on the net and I found that I need to have a standard .pem key maybe, so my question is: How can I convert my key to pem format? Or can I generate a .pem key in Java? And if it's a wrong way, how can I verify my signature?

A PEM file contains the public key binary data encoded in base64 and splitted in lines of 64 characters. The file has also the header -----BEGIN PUBLIC KEY----- and the footer -----END PUBLIC KEY-----
Java has not a native converter to PEM but you can use bouncycastle
PEMWriter pemWriter = new PEMWriter(new FileWriter(file));
pemWriter.writeObject(publicKey);
pemWriter.flush();
pemWriter.close();
Alternatively you can verify a signature with openssl using a binary key format using
-keyform DER
Then save the content of your publicKey in a file
byte publicKeyDer[] = publicKey.getEncoded()

Related

java.security.InvalidKeyException: invalid key format while generating public, private key from PEM file

I have gone through many similar threads but no luck!!
I want to generate public and private keys using a PEM file. Following is the code I am using for the same:
String pemFileNme = "C:\\Users\\amitmm\\Desktop\\clean\\key.pem";
File pubKeyFile = new File(pemFileNme);
File privKeyFile = new File(pemFileNme);
// read public key DER file
DataInputStream dis = new DataInputStream(new
FileInputStream(pubKeyFile));
byte[] pubKeyBytes = new byte[(int)pubKeyFile.length()];
dis.readFully(pubKeyBytes);
dis.close();
// read private key DER file
dis = new DataInputStream(new FileInputStream(privKeyFile));
byte[] privKeyBytes = new byte[(int)privKeyFile.length()];
dis.read(privKeyBytes);
dis.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// decode public key
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubKeyBytes);
RSAPublicKey pubKey = (RSAPublicKey)
keyFactory.generatePublic(pubSpec);
// decode private key
PKCS8EncodedKeySpec privSpec = new
PKCS8EncodedKeySpec(privKeyBytes);
RSAPrivateKey privKey = (RSAPrivateKey)
keyFactory.generatePrivate(privSpec);
Exception:
Exception in thread "main" java.security.spec.InvalidKeySpecException:
java.security.InvalidKeyException: invalid key format
at
sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:205)
at java.security.KeyFactory.generatePublic(KeyFactory.java:334)
at main.java.me.txedo.security.Main2.f1(Main2.java:47)
at main.java.me.txedo.security.Main2.main(Main2.java:20)
Caused by: java.security.InvalidKeyException: invalid key format
at sun.security.x509.X509Key.decode(X509Key.java:387)
PEM File Content:
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwnEEodFEf86+Ae+wYyI//u1kekIWnA3RfzbAwWD77uG7D9Ci
9vVNbPO4XT2hKL03/q7d7KTgrA1sjBltfaOzVfA56x1S/0cYVk4xI440dpLo0F+m
RIqRw5fh8IuUlUIr3I4A7ESkDQQsZbDpdgCiNbrlADqLotcZyB4rU4uURW8QUI/W
eqsD6TOQs4bI+3o3xAKkky2kXujSaaa3tDxgUPTmSQ0Buk7Hx/IVzwyV7qjWiR4U
C46rHpnWxfF0DWuJUOYgJmBQ8xFOQwt4Ec/u+0m8top8cqQF+gpBn9iLXpbtahA3
pqyvLuNXRH9yn8mlEneBrjjl6U0H3W/AV7/dGwIBAwKCAQEAgaCtwTYtqonUAUp1
l2wqqfOYUYFkaAk2VM8rK5X9SevSCosXT04znffQPikWGyjP/x8+ncNAcrOdsrue
U8J3jqAmnL43VNoQOYl2F7Qi+bdF4D/ELbG2gmVBSwe4Y4FykwlV8thtXgLIQ8tG
TqsWznyYqtGybI9mhWlyN7Ji2POMDZP5Lwx7M01pMezwpnsZSmPVL9TgVrtWv4xt
C0vPyuy9THlFWtkOdHItNK+vOTcpuHn29rFUJI/D3R+SQjcdqj3aaqljOtdeBxgd
yDl2/Z4rUyetgzcZMfNTt/NRT0hOJ6R6/2S7gFCTtxMHBh3vVCH+pLLnQyJvcPQu
AsORSwKBgQDhOPr1x/8BioqaasoXvO9NsGktCgPDjbC4d3jR8n6lCa42X/eIahaD
xi1VGWyQhdO7aMXiDmzOtox7xHcMRh+a5ySIs9gTsHkMB2hqwIUNg25INRkQ3Vr3
eWnoTBGsfJqC1TEME3ocKwmyz57ZAe4yyR/ZRdDX5DUt9qCCFeA8uQKBgQDdAzbq
7BlJkbTYfdlIRNJEJAO3wWqQTx8X0ttCMMwDluOT9l+RR/KuUxl85ph+kwJci6E/
ixfeMTW1NcsMY/lB6mTP0oooalU1MP7gpPSu+24zhLXnUHZotbNbv9nk6w/1WWhz
FBt5w2DG4kQPFK6LSySqcVuzIGQyvWD5PbpGcwKBgQCWJfyj2qoBBwcRnIa6ffTe
dZtzXAKCXnXQT6XhTFRuBnQkP/pa8WRX2XOOEPMLA+J88IPsCZ3fJF2n2E9dhBUR
722wd+VidaYIBPBHKwNeV57azhC16OdPpkaa3WEdqGcB43YIDPwSx1vMimnmAUl3
ML/mLos6mCNz+cBWuUAoewKBgQCTV3nx8ruGYSM6/pDa2IwtbVfP1kcK32oP4eeB
dd1Xue0NTupg2qHJjLuombr/DKw9smt/sg/pdiPOI9yy7VDWnEM1NwbFnDjOIKnr
GKMfUkl3rc6aNaRFzneSf+aYnLVOO5r3Yrz715XZ7C1fYx8Hh23G9j0iFZgh05X7
fnwu9wKBgHyC0X26KZQ0ukan5jDSiz4dapUp2d3F+vnRzZa2AOsmo995gsXLdfsJ
n0o4Z3LsQJUDRI3tQ4dXe/5jS4oFrOdxALOAw6YmvEv/3oHwsCYPDhqLNfIJ9I6m
Dt3yG61pUJiCArhPaYG17NQoCxF6Xi6GUajRsECbr8DdyGMAu5eE
-----END RSA PRIVATE KEY-----
I have tried removing file header and footer manually. I tried code from bouncycastle, no luck, same error.
Python code which works with this file:
def t2e_enc(plaintext, pk_pem_file = './2017-12-04T062008Z.pem'):
'''
Function for encryption of Track2 credit card data.
This function uses private key to derivate public part used for encryption
'''
with open(pk_pem_file, 'rb') as pk:
private_key = serialization.load_pem_private_key(pk.read(),
password=None, backend=default_backend())
public_key = serialization.load_pem_public_key(
private_key.public_key().public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.SubjectPublicKeyInfo),
backend=default_backend()
)
ciphertext = public_key.encrypt(
plaintext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(),
label=None
)
)
b64ciphertext=base64.b64encode(ciphertext)
return b64ciphertext
I am doing this for the first time, so bear with me if there is any silly mistake.
Partial dupe Load RSA public key from file
So, you 'wrote' (I assume, copied) code that clearly says you need two files, in DER form, containing PKCS8 and 'X509' encodings. (What Java calls X.509 here is really the SubjectPublicKeyInfo component of X.509.) You give it one file, in PEM form not DER, containing a PKCS1 encoding not PKCS8 or X509 -- and you're surprised it doesn't work? Python works because it calls OpenSSL, and OpenSSL supports over a dozen encodings and formats for privatekeys, including this one; Java supports only one (outside of keystores) which isn't this one. (Bare) publickeys are a little better; internally libcrypto supports multiple forms, but in practice only two of them are used, and one of them matches Java -- although many publickeys are distributed, stored, and used in the form of X.509 certificates, which provides several more forms to worry about.
There are approximately 7 solutions to your situation:
the simplest is to use OpenSSL commandline to convert your one file to the two files Java wants:
# (corrected! pkey is inconsistent!)
openssl pkcs8 -topk8 -nocrypt -in input.pem -outform der -out private.der
openssl pkey -in input.pem -pubout -outform der -out public.der
# or for very old versions (should not be needed now)
openssl rsa -in input.pem -pubout -outform der -out public.der
Those files can now be read by the code you posted (except with the filenames separated). Note this conversion doesn't have to be done on the same system; if necessary you can do it elsewhere and copy the files, if you use a method that works for binary files i.e. NOT cut&paste.
if you really want only one file, but it can be converted, create the private.der file as above and read it with only the privatekey-related parts of your code, then do:
RSAPrivateCrtKey priv2 = (RSAPrivateCrtKey)privKey;
PublicKey pubkey = keyFactory.generatePublic(new RSAPublicKeySpec(priv2.getModulus(), priv2.getPublicExponent()));
you could convert the files to PKCS8 and 'X509' PEM by omitting -outform der from the above conversions, then read those files and manually 'de-PEM' by removing the header and trailer lines and converting the base64 to binary (removing or skipping the linebreaks); this results in binary PKCS8 and X509 encodings you can run through your existing code. This is as much work on the openssl side and more work on the Java side so there is no apparent advantage, except that PEM files are valid text and can be cut&pasted if necessary.
combining these, you could convert to PKCS8 PEM only, read that per bullet 3 (de-PEM then the privatekey parts of your code), then extract publickey from privatekey per bullet 2
one way to use the format you have (unconverted) in plain Java is to de-PEM per bullet 3 giving you a PKCS1 encoding, then manually construct the PKCS8 encoding, then proceed as before to run the PKCS8 through the KeyFactory and extract publickey per bullet 2. See my answer at Java: Convert DKIM private key from RSA to DER for JavaMail for a really ugly way to do this (including one de-PEM method). There is a better way if you use BouncyCastle (which has a class for this ASN.1 type), but if you use BouncyCastle it's better not to use this method at all, see below.
another way to use the unconverted format in plain Java is to de-PEM per bullet 3, then parse the ASN.1 structure of PKCS1 and construct an RSAPrivateCrtKeySpec which you can run through your KeyFactory instead of a PKCS8 encoding then extract publickey per bullet 2. This is even more complicated, although I think I have seen it somewhere; will add if I find it. Again BouncyCastle can improve this method, but doesn't need to, see below.
finally, if you have BouncyCastle it's dead easy. You don't say what you tried with 'no luck', but the following BouncyCastle code is all you need and does work:
try( Reader r = new FileReader(filename) ){
KeyPair pair = new JcaPEMKeyConverter().getKeyPair((PEMKeyPair)new PEMParser(r).readObject());
}
Note this gives you a KeyPair with both privatekey and publickey objects from the one file.

How can I convert PEM file to string for ssh to ec2 using JSch library

I am trying to SSH to EC2 using JSch Library from Java code. I referred this link in SO How can I use .pem files content as a string in ec2 connection using JSch library and tried couple of things as mentioned below but in vain. Can someone please guide me on how to achieve my objective?
Objective
I have a PEM file like this. I dont want to store my PEM file anywhere in AWS, hence my approach is to extract an equivalent string that I can encode and store in database and decode it from java for passing the parameter to addIdentity method that takes these parameters:
addIdentity(String name, byte[] prvkey, byte[] pubkey, byte[] passphrase)
throws JSchException
-----BEGIN RSA PRIVATE KEY-----
MIIepsdfAIBAAKCAQEAtBk068z
...
xVNdhlDy6asdk9wsdQ==
-----END RSA PRIVATE KEY-----
For my objective, my addIdentity method would be like this I believe:
addIdentity ("username","{privatekey string converted to byte array}",null, null)
I am trying to understand how that string can be formed? I am very new to cryptography, but during this process I learnt that since my PEM has BEGIN RSA PRIVATE KEY, it's PKCS1 format. Does JSch support PKCS1 format or it needs to be converted to PKSC8?
Secondly, I learnt that the body is encoded with Base64, so I even tried decoding the string with Base64 after stripping off all the carriage returns, header and footer, which gave me error like this
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
Below are some of the additional links I tried following up but have not been able to resolve.
JSch getting "invalid privatekey:" while trying to load an RSA private key by KeyPairGenerator
Java - How to decode a Base64 encoded Certificate
Converting a PEM private key file to a JAVA PrivateKey Object
Hope someone can guide me in the right direction.
Thanks!
I figured out the answer.
Below post gave me a direction.
JSch: addIdentity from private key stored on hdfs
To anyone else who is looking to solve a similar requirement, ensure that you are not stripping off the header, footer information. This took most of my time to debug as most of the blogs/SO posts directed towards stripping those characters. In Java, your string must have the carriage returns else you might get a very different byte array.
String x = "-----BEGIN RSA PRIVATE KEY-----\r\n" +
"MIIEpAIBAAKCAQEAtBk\Q/z4QAgk+LN3IUajqjUv7IucsCd4SebbQvah5t4WJ\r\n"
Convert the string to byte array using "US-ASCII" charset. Use following JSch method if you don't have a passphrase:
jsch.addIdentity("username",{bytearray of x},null, null)
Note: ensure that you are passing an unsigned byte array like:
Array (45, 45, 69,...)
and NOT
Array (45, -35, -125,...)

Convert certificate string to byte array

I got a string represents PEM certificate:
-----BEGIN CERTIFICATE-----
MIICUTCCAfugAwIBAgIBADANBgkqhkiG9w0BAQQFADBXMQswCQYDVQQGEwJDTjEL
MAkGA1UECBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMC
VU4xFDASBgNVBAMTC0hlcm9uZyBZYW5nMB4XDTA1MDcxNTIxMTk0N1oXDTA1MDgx
NDIxMTk0N1owVzELMAkGA1UEBhMCQ04xCzAJBgNVBAgTAlBOMQswCQYDVQQHEwJD
TjELMAkGA1UEChMCT04xCzAJBgNVBAsTAlVOMRQwEgYDVQQDEwtIZXJvbmcgWWFu
ZzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCp5hnG7ogBhtlynpOS21cBewKE/B7j
V14qeyslnr26xZUsSVko36ZnhiaO/zbMOoRcKK9vEcgMtcLFuQTWDl3RAgMBAAGj
gbEwga4wHQYDVR0OBBYEFFXI70krXeQDxZgbaCQoR4jUDncEMH8GA1UdIwR4MHaA
FFXI70krXeQDxZgbaCQoR4jUDncEoVukWTBXMQswCQYDVQQGEwJDTjELMAkGA1UE
CBMCUE4xCzAJBgNVBAcTAkNOMQswCQYDVQQKEwJPTjELMAkGA1UECxMCVU4xFDAS
BgNVBAMTC0hlcm9uZyBZYW5nggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEE
BQADQQA/ugzBrjjK9jcWnDVfGHlk3icNRq0oV7Ri32z/+HQX67aRfgZu7KWdI+Ju
Wm7DCfrPNGVwFWUQOmsPue9rZBgO
-----END CERTIFICATE-----
I assigned the above string to String variable String myCertStr.
What is the proper way to convert myCertStr to DER encoded byte[]?
(I am using Java 7, and I am not interested to use 3rd party library for this, I am seeking for a JDK7 way of doing it.)
IMPORTANT
As #dave_thompson_085 has pointed out in the comments, SunJCE CertificateFactory is indeed capable of parsing PEM files.
So you can just use that to get the Certificate object
as detailed at How to load public certificate from pem file..? (which is an earlier answer by #dave on the same topic, so please upvote it, instead of this one, if you find this useful !), and then access its encoded (DER) form.
However, if your PEM file is a raw "RSA PUBLIC KEY" (like the one that was attached to this question), or some other entity which SunJCE implementation can not parse directly, you can still parse and decode it manually, as detailed below.
Technically what you have here is not a certificate, but just a public key.
You can decode it to DER bytes as simple as that:
byte[] derBytes = Base64.getDecoder().decode(
pemText.replaceAll("-----(BEGIN|END) RSA PUBLIC KEY-----", "").replaceAll("\n", "")
);
Note, that what you will get will be a raw RSA (PKCS#1) key:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
You can use the same technique to decode X.509 certificates or private keys.
E.g. the code to decode the X.509 certificate:
byte[] certificateBytes = Base64.getDecoder().decode(
pemText.replaceAll("-----(BEGIN|END) CERTIFICATE-----", "").replaceAll("\n", "").getBytes("UTF-8")
);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate)(certificateFactory.generateCertificate(
new ByteArrayInputStream(certificateBytes)
)
);
UPDATE
The code above uses Java 8 Base64 decoder.
As question has been updated asking for a Java 7 solution,
here is a link to an excellent thread, discussing various options available: Base64 Java encode and decode a string.
E.g. java.xml.bind method described there does not require any extra libraries on Java 7 (which seems to match what OP wants)

Detect PEM file content: certificate or public/private key?

I am trying to detect the content of a PEM file. In case it contains certificate infos, the following code is perfect to get X509Certificates informations:
X509Certificate cert = X509CertUtils.parse(Files.readAllBytes(file.toPath()));
but returns NULL of course if it's a key instead of certificate.
So is there a proper way to detect PEM content (mayb PEMReader and then instance of on the result. I tried with a ---- PRIVATE KEY ----- file but it returned null also)?
Of course, I can manually detect if file contain -----BEGIN CERTIFICATE----- or -----BEGIN PRIVATE KEY----- but I find this very careless.
Thank you !

Help verifying RSA signed text with Python

Using Java I have created RSA keypairs. Using Java I can use these keys to sign and verify some text. I can also "export" these keys in PEM format and load them into a Python test script. Once in the Python script, I can use these keys to sign and verify some text using M2Crypto.
I have not yet been able to verify in Python the signature I created in Java.
Right now I am just trying to get cross-platform signing and verifying to work.
Here is Java snippet:
Signature sig = Signature.getInstance("MD5WithRSA");
sig.initSign(key.getPrivate());
sig.update("This is a message.".getBytes("UTF8"));
byte[] signatureBytes = sig.sign();
return Base64.encodeBytes(signatureBytes, Base64.DO_BREAK_LINES);
Which generates:
PIp4eLhA941xmpdqu7j60731R9oWSNWcHvwoVADKxABGoUE02eDS0qZ4yQD2vYBdRDXXxHV4UjtW
YQwv9nsOzCBWeDQ0vv6W0dLVfTBuk79On7AALuwnTFr8s0y5ZN5RINvPPR60mwONav26ZbPj4ub3
NZqUS/zkqyO8Z8D2zUjk0pqAhWDGbFBaWPQJBPOY9iRt8GlsAUkGfYGeIx9DNU8aiJmQ3NnUHbs4
5NEr3xydbNJjwK96kkNJ9vyKZRfnNd4eW2UllPiwJSRQgefCQfh79ZuiYeQEuk3HMh7Si4iYl7uU
rWCgYFl4fGV1X/k+BSHR4ZZFWGQ3IPfafYHyNw==
And here is the public key:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg+N7xQHVHU3VHMZ9VGFdUf6ud3rAL8YS
sfWv2zFMnKAjh6XacwDGX6jJR/0/pyDriRGw/uodBDSFvgn9XEM/srMYtbJ1KQ9R9ChDifixF3Hx
yzbzPRvFoEfZnS/63+d1r0wnafz7xx0eDEYE3TgRKTMikwOsuIOFWa7GhA4BvP7ad79bI5ORJdLu
9Je+k+4dbt0xk2t7YopxYmyU+4zhZMxuthJTr69rXgqhgsCRdK6kFIGm3YuJ1WQcci8OVwH+3o7F
XzJHpSTxH57m6PX5lXaywIDCbUauTpBV3w+0vTeGI/2o+U40qhLBkpZT9GSVKxgXl5a0XxrkwTGn
61XZqQIDAQAB
-----END PUBLIC KEY-----
Then in Python the key is loaded and the signature is attempted to be verified:
from M2Crypto import RSA, EVP
pub_key = RSA.load_pub_key('public_key.pem')
verify_evp = EVP.PKey()
verify_evp.assign_rsa(pub_key)
verify_evp.verify_init()
verify_evp.verify_update("This is a message.")
if verify_evp.verify_final(sig_string.decode('base64')) == 1:
print "Good"
else:
print "Bad"
And this does not verify. I suspect it is some leading or trailing characters or encoding weirdness that I don't understand.
I have no particular attachement to M2Crypto and have played a bit with the gdata.tlslite modules as well.
Please note that the keys are working, and that the text and signature above verify in Java, and the keys (public and private) can be used within Python to sign and verify text. The problem is somewhere in how Java-generated signature and/or the message text is getting into the Python code.
What am I doing wrong?
M2Crypto.EVP defaults to 'sha1' (SHA1) and you're using MD5WithRSA. I think you should switch SHA1WithRSA (MD5 is a very weak algorithm).

Categories

Resources