DER Encode BCECPublicKey - java

I'm trying to Der encode a public key, and use it with an external service.
When I get the encoded org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey and send it to the external service, it responds with "ECDSA certificates with unnamed curves are unsupported". (I'm calling publicKey.getEncoding() to get the encoded key)
publicKey.getFormat();
// "X.509"
publicKey.getAlgorithm();
// "ECDH"
publicKey.getQ().curve.getClass().name;
// "org.bouncycastle.math.ec.custom.sec.SecP256R1Curve"
I'm not really sure how to debug from here. I tried saving the raw encoded bytes
to a file and inspecting the cert with openssl without success:
> openssl x509 -in test.der -inform der -text -noout
unable to load certificate
62375:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64.30.2/src/crypto/asn1/tasn_dec.c:1344:
62375:error:0D06C03A:asn1 encoding routines:ASN1_D2I_EX_PRIMITIVE:nested asn1 error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64.30.2/src/crypto/asn1/tasn_dec.c:848:
62375:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64.30.2/src/crypto/asn1/tasn_dec.c:768:Field=serialNumber, Type=X509_CINF
62375:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/OpenSSL098/OpenSSL098-64.30.2/src/crypto/asn1/tasn_dec.c:768:Field=cert_info, Type=X509
Any help would be greatly appreciated!

Here's the solution I came up with:
KeyFactory keyFactory = KeyFactory.getInstance("EC");
X509EncodedKeySpec keySpec = keyFactory.getKeySpec(publicKey, X509EncodedKeySpec.class);
return keySpec.getEncoded();

Related

How to convert Curl SSL requests into Java code

Curl allows making 2-way SSL requests by specifying the certificate and private key files like the two curl requests below
First get an access token:
$ curl https://connect2.server.com/auth/token
-H "Authorization: Basic $BASIC_AUTH"
--cert ~/certs/certpath/fullchain.pem
--key ~/certs/certpath/privkey.pem
Then use the token to access the API:
$ curl https://connect2.server.com/api/public/preview1/orgs/$ORGUUID/API
-H "Authorization: Bearer $ACCESS_TOKEN"
--cert ~/certs/certpath/fullchain.pem
--key ~/certs/certpath/privkey.pem
Question:
How to implement the above requests in Java? What libraries are required? Java seems to use p12 file, however, we have .pem files.
‌1. You can convert PEM privatekey plus chain to a PKCS12 file using openssl pkcs12 -export. That is not programming or development and no longer ontopic here, but there are dozens of Qs about this here going back many years when topicality was broader, as well as in other Stacks (security.SX, serverfault, superuser, maybe more).
‌2. If you don't have or dislike OpenSSL, you can read those files (among others) into any kind of Java keystore (JCEKS, JKS, PKCS12, and several BouncyCastle variants you probably don't want) using the software from https://www.keystore-explorer.org . That's also offtopic, and I've seen some existing Qs mention it but not many.
‌3. If you want to do this with your own code, which is ontopic, and assuming your curl uses OpenSSL or at least those files are OpenSSL formats:
3.0 Java can read PEM cert sequence with CertificateFactory.getInstance("X.509") then generateCertificates(InputStream) (note s) -- the doc is a bit sketchy but this method actually can handle separate certs as DER or PEM (which you apparently have), or PKCS7 containing certs as a single blob (commonly called p7b or p7c) ditto.
3.1 if that privkey file is PKCS8-unencrypted, i.e. if the PEM labels are BEGIN/END PRIVATE KEY with no other word between, that case can be handled by standard Java, assuming you know what algorithm it is for (which if necessary you can determine from the first=leaf certificate). Delete the BEGIN/END lines and decode the rest from base64 to binary either ignoring linebreaks (with Base64.getMimeDecoder()) or with .getDecoder() after deleting the linebreaks. Put the result in PKCS8EncodedKeySpec and feed it to generatePrivate in a KeyFactory instance for the correct algorithm.
3.2 BouncyCastle (bcpkix+bcprov) can read all the PEM formats for privatekey used by OpenSSL with PEMParser and JcaPEMKeyConverter and if applicable a DecryptorBuilder. There are many existing Qs on this that you can find with that fairly-unique classname. This does mean a dependency on Bouncy.
3.3 if you don't have or don't want Bouncy and have a format other than PKCS8-unencrypted, life gets harder. You could avoid this by using OpenSSL to convert the privkey file to PKCS8-unencrypted putting you back in #3.1, but if you do that you might as well go way back to #1 and use OpenSSL to convert the lot to PKCS12 in one foop.
if you have an OpenSSL 'traditional' algorithm-specific format like BEGIN/END RSA PRIVATE KEY or BEGIN/END EC PRIVATE KEY, and the first two lines after BEGIN are NOT Proc-type: 4 and DEK-info, you can base64-decode the body and convert it to PKCS8 by adding a (DER) prefix in front that specifies the algorithm and 'wraps' the algorithm-specific part. I think there are dupes for this but I can't presently find any; if this case applies and you identify the algorithm I'll add it.
if you have a 'traditional' format that does have Proc-type: 4 and DEK-info, or you have BEGIN/END ENCRYPTED PRIVATE KEY, those are encrypted. Making sense of them with only standard Java is a fair bit of work which I'll do only if you can't use the other options and specify exactly what case you have.
Following are the steps & code to add SSL certificates into HTTP Post request.
STEP 1. CONVERT PEM CERTIFICATE TO P12 FORMAT
openssl pkcs12 -export -out cacert.p12 -inkey /etc/letsencrypt/archive/server/privkey21.pem -in /etc/letsencrypt/archive/server/cert21.pem -certfile /etc/letsencrypt/archive/server/chain21.pem -passin pass:PWD -passout pass:PWD
STEP 2. (OPTIONAL NOT REQUIRED) CONVERT CERTIFICATE P12 TO JKS FORMAT
keytool -importkeystore -srckeystore cacert.p12 -srcstoretype pkcs12 -destkeystore cacert.jks
STEP 3. ADD CERTIFICATE TO HTTP POST REQUEST THROUGH SSLSocketFactory
/**
* This function is responsible to create createSSLSocketFactory with SSL certificate
* #return
*/
public static SSLSocketFactory createSSLSocketFactory(){
try
{
FileInputStream f5 = new FileInputStream(new File("/etc/letsencrypt/archive/server/cacert21.p12"));
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore ks1 = KeyStore.getInstance(KeyStore.getDefaultType());
ks1.load(f5, "PWD".toCharArray());
kmf.init(ks1, "PWD".toCharArray());
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(kmf.getKeyManagers(), null, null);
f5.close();
return sslContext.getSocketFactory();
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}

Convert java cipher decrypt to python equivalent

I'm trying to convert the below java code to python:
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
serverCertificate = (X509Certificate) certFactory.generateCertificate(new FileInputStream(certFile));
Cipher decryptCipher = Cipher.getInstance("RSA");
decryptCipher.init(Cipher.DECRYPT_MODE, serverCertificate.getPublicKey());
byte[] decryptedClientChallengeBytes = decryptCipher.doFinal(encryptedClientChallengeBytes);
The first part I think it's resolved with this:
from cryptography import x509
from cryptography.hazmat.backends import default_backend
cert = x509.load_pem_x509_certificate(key,default_backend())
cert = cert.public_key()
But I can't convert the decrypt part. I´ve tried various methods like PKCS1_v1_5.
I'm trying to implement this https://docs.oracle.com/en/cloud/saas/marketing/responsys-rest-api/RequestUserAuthorization.html
And the step i'm stacked says: "You should receive the following response from the server. Decrypt (using the RSA algorithm) the encrypted clientChallenge using server certificate's public key (which you should have downloaded and stored on your system)."
The clientChallenge received is like this:
"hc8w8gElQJQa45WGofZwsOAi4wwEIaFClI1A2PvvCw4YUc4rbnOpedkBgrNsr0FpZ6DVV_vnZNsAGg_ph75VqeKD-R-cy_RkQIdvn0W_nb0RvPGZI7zA98_t9-rJTiTSsrJ6j8KzlWV_MLydUcBBR1iotL6kXXuFKuXtTAniPx_b6do4D5p9iI7yhTfxp9knRRG6XoItgNGJwm8AnGsThIoZw9lSgUm3Dy9yzYvhXQDYKAUNc3tqd8WcSswlb5coN18Gc-We2E0xE7xehq15Ns4QNwX6WdRrfehSgOObdckUYAx7MyLT-vyfJ-APVEVrZmC18wo-Qr2A-n1NT0ARVQ"
And the server certificate is a .CER with this format:
-----BEGIN CERTIFICATE-----
.....
-----END CERTIFICATE-----
Any help would be awesome!

Java equivalent to 'openssl rsautl -verify -out'

I have some data which has been signed using something like the following:
openssl rsautl -sign -in file -inkey PrivateKey.pem -out sig
The signed data can then be recovered using the following:
openssl rsautl -pubin -inkey PublicKey.pem -verify -in sig -out file
I've converted the public key into a Java PublicKey object and looked at using that with the Signature class in java.security, but that doesn't appear to have a method that allows recovering the original signed data.
Is there a way to recover the signed data using Java?
As suggested in the comment by dave_thompson_085, the following can be used to recover the data:
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, publicKey);
byte[] extractedBytes = cipher.doFinal(sig);

InvalidKeyException when creating PublicKey in Java from openssl ec

I get a InvalidKeyException: invalid key format when creating a java.security.PublicKey from a PEM file generated by openssl ec -pubout ....
The same code works for RSA keys.
What am I doing wrong?
The public key reads:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAG0FCGgyhUeJYUXeXoiKOU4GiyTORZ
U9+OpadxpVWqPbNoSNcfK7Ea13eWOKXlUe22v4Clce3t5nrCEBkwqhhe/g==
-----END PUBLIC KEY-----
EC key generation with OpenSSL:
openssl ecparam -genkey -out private_key.pem -outform PEM -name prime256v1
openssl pkcs8 -topk8 -inform PEM -outform DER -in private_key.pem -out private_key.der -nocrypt
openssl ec -in private_key.pem -inform PEM -out public_key.pem -outform PEM -pubout
(I already tried different settings for conv_form and param_enc)
Java code:
KeyFactory kf = KeyFactory.getInstance("EC");
byte[] privEncoded = ... // read from private_key.der file
PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privEncoded);
PrivateKey privKey = kf.generatePrivate(privSpec);
byte[] pubEncoded = .... // read from public_key.pem file
X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubEncoded);
PublicKey pubKey = kf.generatePublic(pubSpec); // <-- InvalidKeyException
Edit: Contrary to what I said above, reading the RSA public key now fails too when trying it with a newly generated PEM. Also, encrypting and then decrypting fails. Stripping the BEGIN and END line from the PEM and converting Base64 to byte, as suggested by GregS, solved it!
The so-called "PEM" format is not supported by Java. You must either save the data in openssl's "DER" format or strip out the first and last lines and decode the base64 in your Java program. See javax.xml.bind.DataTypeConverter.parseBase64Binary to go from base64 to bytes.

Decrypting encrypted assertion using SAML 2.0 in java using OpenSAML

I have a problem while trying to decrypt encrypted assertion using SAML 2.0. The library I am using is OpenSAML Java libraries 2.5.2.
The encrypted assertion looks like this:
<EncryptedAssertion xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
<enc:EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
xmlns:enc="http://www.w3.org/2001/04/xmlenc#">
<enc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" />
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
</e:EncryptionMethod>
<KeyInfo>
<o:SecurityTokenReference
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-
1.0.xsd">
<o:KeyIdentifier
ValueType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-
1.1#ThumbprintSHA1"
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-
message-security-1.0#Base64Binary">
1H3mV/pJAlVZAst/Dt0rqbBd67g=
</o:KeyIdentifier>
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>
... ENCRYPTED KEY HERE ...
</e:CipherValue>
</e:CipherData>
</e:EncryptedKey>
</KeyInfo>
<enc:CipherData>
<enc:CipherValue>
... ENCRYPTED ASSERTIONS HERE ...
</enc:CipherValue>
</enc:CipherData>
</enc:EncryptedData>
</EncryptedAssertion>
I did convert my private key that is in PEM format to pkcs8 format using the following openssl command:
openssl pkcs8 -topk8 -nocrypt -inform PEM -in rsa_private_key.key -outform DER -out rsa_private_key.pk8
I am then ready to try to decrypt the encrypted assertion. Here is my Java code:
...
// Load the XML file and parse it.
File xmlFile = new File("data\\token.xml");
InputStream inputStream = new FileInputStream(xmlFile);
Document document = parserPoolManager.parse(inputStream);
Element metadataRoot = document.getDocumentElement();
// Unmarshall
UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();
Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(metadataRoot);
EncryptedAssertion encryptedAssertion = (EncryptedAssertion)unmarshaller.unmarshall(metadataRoot);
// Load the private key file.
File privateKeyFile = new File("data\\rsa_private_key.pk8");
FileInputStream inputStreamPrivateKey = new FileInputStream(privateKeyFile);
byte[] encodedPrivateKey = new byte[(int)privateKeyFile.length()];
inputStreamPrivateKey.read(encodedPrivateKey);
inputStreamPrivateKey.close();
// Create the private key.
PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
RSAPrivateKey privateKey = (RSAPrivateKey)KeyFactory.getInstance("RSA").generatePrivate(privateKeySpec);
// Create the credentials.
BasicX509Credential decryptionCredential = new BasicX509Credential();
decryptionCredential.setPrivateKey(privateKey);
// Create a decrypter.
Decrypter decrypter = new Decrypter(null, new StaticKeyInfoCredentialResolver(decryptionCredential), new InlineEncryptedKeyResolver());
// Decrypt the assertion.
Assertion decryptedAssertion;
try
{
decryptedAssertion = decrypter.decrypt(encryptedAssertion);
}
...
Running this code always results as being unable to decrypt the assertion. I do get the following errors:
5473 [main] ERROR org.opensaml.xml.encryption.Decrypter - Error decrypting encrypted key
org.apache.xml.security.encryption.XMLEncryptionException: Key is too long for unwrapping
Original Exception was java.security.InvalidKeyException: Key is too long for unwrapping
at org.apache.xml.security.encryption.XMLCipher.decryptKey(Unknown Source)
at org.opensaml.xml.encryption.Decrypter.decryptKey(Decrypter.java:681)
at org.opensaml.xml.encryption.Decrypter.decryptKey(Decrypter.java:612)
at org.opensaml.xml.encryption.Decrypter.decryptUsingResolvedEncryptedKey(Decrypter.java:762)
at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:513)
at org.opensaml.xml.encryption.Decrypter.decryptDataToList(Decrypter.java:440)
at org.opensaml.xml.encryption.Decrypter.decryptData(Decrypter.java:401)
at org.opensaml.saml2.encryption.Decrypter.decryptData(Decrypter.java:141)
at org.opensaml.saml2.encryption.Decrypter.decrypt(Decrypter.java:69)
at DecrypterTool.main(DecrypterTool.java:121)
java.security.InvalidKeyException: Key is too long for unwrapping
at com.sun.crypto.provider.RSACipher.engineUnwrap(DashoA13*..)
at javax.crypto.Cipher.unwrap(DashoA13*..)
at org.apache.xml.security.encryption.XMLCipher.decryptKey(Unknown Source)
at org.opensaml.xml.encryption.Decrypter.decryptKey(Decrypter.java:681)
at org.opensaml.xml.encryption.Decrypter.decryptKey(Decrypter.java:612)
at org.opensaml.xml.encryption.Decrypter.decryptUsingResolvedEncryptedKey(Decrypter.java:762)
at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:513)
at org.opensaml.xml.encryption.Decrypter.decryptDataToList(Decrypter.java:440)
at org.opensaml.xml.encryption.Decrypter.decryptData(Decrypter.java:401)
at org.opensaml.saml2.encryption.Decrypter.decryptData(Decrypter.java:141)
at org.opensaml.saml2.encryption.Decrypter.decrypt(Decrypter.java:69)
at DecrypterTool.main(DecrypterTool.java:121)
5477 [main] ERROR org.opensaml.xml.encryption.Decrypter - Failed to decrypt EncryptedKey, valid decryption key could not be resolved
5477 [main] ERROR org.opensaml.xml.encryption.Decrypter - Failed to decrypt EncryptedData using either EncryptedData KeyInfoCredentialResolver or EncryptedKeyResolver + EncryptedKey KeyInfoCredentialResolver
5478 [main] ERROR org.opensaml.saml2.encryption.Decrypter - SAML Decrypter encountered an error decrypting element content
org.opensaml.xml.encryption.DecryptionException: Failed to decrypt EncryptedData
at org.opensaml.xml.encryption.Decrypter.decryptDataToDOM(Decrypter.java:524)
at org.opensaml.xml.encryption.Decrypter.decryptDataToList(Decrypter.java:440)
at org.opensaml.xml.encryption.Decrypter.decryptData(Decrypter.java:401)
at org.opensaml.saml2.encryption.Decrypter.decryptData(Decrypter.java:141)
at org.opensaml.saml2.encryption.Decrypter.decrypt(Decrypter.java:69)
at DecrypterTool.main(DecrypterTool.java:121)
I really don't know what I'm doing wrong in this case. I converted my private key to pkcs8, I loaded my SAML XML data and unmarshalled it into the valid type (EncryptedAssertion) and I created a decrypted based on my private key.
Is it possible that it is related to the oaep format for RSA? I'm using the default java cryptography library.
Thanks!
For those of you who will get this problem, it was related to the fact that the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files was not installed and it was not letting me use encryption better than AES-128. Replacing the policy files with the JCE policy files, I was able to successfully decrypt my encrypted assertion.
Agree with #thwalrusnp. Just wanted to add the exact location from where you can download the policy jars.
Found it on the answer to Error while decrypting assertion sent from IDP
This happens due to limitation of cryptography strength in default
distribution of Java Runtime Environment.
Download Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files (for Java 7) (for Java 8)
Extract zip archive and find there local_policy.jar and US_export_policy.jar.
Replace your JRE version of these files under $JAVA_HOME/jre{version_number}/lib/security/ with downloaded ones.
Restart JRE process, if any running. Now you can use longer keys.

Categories