Read encrypted PKCS8 SpongyCastle Java - java

I am following the code in: https://stackoverflow.com/a/18161536/1753951 but I am getting an Exception in the following line:
FileInputStream fis = new FileInputStream(priv);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int)priv.length()];
dis.readFully(keyBytes);
dis.close();
javax.crypto.EncryptedPrivateKeyInfo encryptPKInfo = new EncryptedPrivateKeyInfo(keyBytes);
//Exception:
org.apache.harmony.security.asn1.ASN1Exception: Wrong content length
I am trying to read a .key/.pem PKCS8 file which is:
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK Info: AES-256-CBC,8AFF348907C84F2F6370A216DC0D55D9
1VIjJD3dZ5/wYnIm0mtp8d22RC24yGcY9LXgeHUDbyPJQa8PjupubFqKrpOodvQx
dPfE1F3XeY8oVG42ZfR4287X4V16n++BQCeDiuvyrwacLMAuQz6PFLT4b/Py89Cm
761UZpaWnH0PHfJqB9CHqC+pGAGfRF5vj7UtdNchCwBmo+7gvU5iGyYXNRJ/hPnU
V+8QDzro4kFIMOlDzHaJ3KN1Ftbb9LDjDNE/NShbRrAFAWJMZSY/ZjF8mfqggkoZ
%%%%% SKIPPED MOST OF IT %%%%%%%%%%
BMIl0y5XVgPwkApA30EdgV4YAZEJ+wQLnYIZfCklqzvCfyjxHFViVW6d41WNm8bx
wl28v4QJKlnf7KNcmmGwSmjKo7BEASSZ+XVYRu0R6FaE+Job5YzPrtUI+p/kf7et
Y+jUDbZ4BPvB8j2ZscNRs+pJkEXxPt5JKW/oQMQZPlbTtSV5K1IqiuVcRi9TbCzk
nWDSfI/wxt6cK3X9XvyOpOZDCDPchkIhDhCzfitd7fzkM1VBekwsliJwjgc1bwbc
nI4AhQcNb8li7oX1M2osyeR3zF25BDb2A04Zm1lMrWkFrypb24DKkSJxYEH33Gpu
-----END RSA PRIVATE KEY-----

After looking long time for a solution I stumbled with a library that helps me and works on android.
Not-Yet-Commons
http://juliusdavies.ca/commons-ssl/
FileInputStream in = new FileInputStream( "/path/to/pkcs8_private_key.der" );
PKCS8Key pkcs8 = new PKCS8Key( in, "changeit".toCharArray() );
byte[] decrypted = pkcs8.getDecryptedBytes();
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec( decrypted );
// A Java PrivateKey object is born.
PrivateKey pk = null;
if ( pkcs8.isDSA() )
{
pk = KeyFactory.getInstance( "DSA" ).generatePrivate( spec );
}
else if ( pkcs8.isRSA() )
{
pk = KeyFactory.getInstance( "RSA" ).generatePrivate( spec );
}
// For lazier types (like me):
pk = pkcs8.getPrivateKey();

javax.crypto.EncryptedPrivateKeyInfo expects a DER-encoded input, while the contents of your file are obviously in the PEM encoding.
The relation between PEM and DER is this:
The DER is the actual ASN.1-encoded data as a sequence of bytes.
The PEM is text-based with all these -----BEGIN SOMETHING----- and -----END SOMETHING----- headers and Base64-encoded data inside. Basically, PEM is header+Base64(DER)+footer.
You need to convert your key into DER format, for example using the OpenSSL pkey command:
openssl pkey -in key.pem -outform DER -out key.der

Related

Convert elliptic curve private key to (unencrypted) PKCS#8 format

I am trying to convert EC private key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIE2tzb8O0gBVw2IFOB/B8l1Ztjax3ut4DeNtuC3UMmZ6oAoGCCqGSM49
AwEHoUQDQgAEayT6Tv8zZlpIUOKHEYnmsKZyTaqOHajL0InS4c5tK4fhkHZDSWUa
3tPl1ibIXt0LvaxHk47h0Tc4SGr3Ex8Bhg==
-----END EC PRIVATE KEY-----
to Private Key
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgTa3Nvw7SAFXDYgU4
H8HyXVm2NrHe63gN4224LdQyZnqhRANCAARrJPpO/zNmWkhQ4ocRieawpnJNqo4d
qMvQidLhzm0rh+GQdkNJZRre0+XWJshe3Qu9rEeTjuHRNzhIavcTHwGG
-----END PRIVATE KEY-----
It's very easy when you execute openSsl command like this:
openssl pkcs8 -topk8 -nocrypt -in ec1.pem -out ec2.pem
But i want to do this in Java way and didn't find any solution (tried so many from stackoverflow).
So i have for now following class:
ECNamedCurveParameterSpec ecNamedCurveParameterSpec = ECNamedCurveTable.getParameterSpec("prime256v1");
KeyPairGenerator keyPair = KeyPairGenerator.getInstance("ECDSA", "BC");
// Create a secure random number generator using the SHA1PRNG algorithm
SecureRandom secureRandomGenerator = SecureRandom.getInstance("SHA1PRNG");
keyPair.initialize(ecNamedCurveParameterSpec, secureRandomGenerator);
Then i generate KeyPair and get PrivateKey in ECPrivateKey Object:
KeyPair pair =keyPair.generateKeyPair();
ECPrivateKey privateKey = (ECPrivateKey) pair.getPrivate();
StringWriter sw = new StringWriter();
try (JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(sw);) {
jcaPEMWriter.writeObject(privateKey);
}
String pemFormat = sw.toString();
This string pemFormat is actually PEM format that starts with BEGIN EC PRIVATE KEY
How can i convert it just to BEGIN PRIVATE KEY?
I assume that should be a way if openSsl can do it.
A conversion from the SEC1/PEM (-----BEGIN EC PRIVATE KEY-----...) to the PKCS#8/PEM format (-----BEGIN PRIVATE KEY-----...) is not needed at all, because privateKey.getEncoded() returns the key already in PKCS#8 format. So it only needs to be exported as PEM e.g. with a PemWriter:
import org.bouncycastle.util.io.pem.PemWriter;
...
// Your code
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
...
ECPrivateKey privateKey = (ECPrivateKey)pair.getPrivate();
System.out.println(privateKey.getFormat()); // PKCS#8
// Export as PKCS#8 PEM encoded key via PemWriter
StringWriter stringWriter = new StringWriter();
try (PemWriter pemWriter = new PemWriter(stringWriter)){
pemWriter.writeObject((PemObjectGenerator)new PemObject("PRIVATE KEY", privateKey.getEncoded()));
}
String pkcs8PEM = stringWriter.toString();
System.out.println(pkcs8PEM); // -----BEGIN PRIVATE KEY-----MIGTAg...-----END PRIVATE KEY-----
You can check the format in an ASN.1 parser, e.g. https://lapo.it/asn1js.
However, if you are really looking for an explicit conversion of a SEC1/PEM key into a PKCS#8/PEM key, then the import of a SEC1/PEM key is described e.g. here. The imported key can then be exported as a PKCS#8/PEM key using a PemWriter as in the example above.

Bouncycastle can't generate private key - Unknown KeySpec type: java.security.spec.X509EncodedKeySpec

I can't generate private key with bouncycastle due to Unknown KeySpec type: java.security.spec.X509EncodedKeySpec. (However doing same for public key doesn't throw exception and works - why?)
java.security.spec.InvalidKeySpecException: Unknown KeySpec type:
java.security.spec.X509EncodedKeySpec at
org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi.engineGeneratePrivate(Unknown
Source) at
java.security.KeyFactory.generatePrivate(KeyFactory.java:366)
PemReader pemReader = new PemReader(new InputStreamReader(new FileInputStream("private_unencrypted.pem")));
PemObject pemObject = pemReader.readPemObject();
pemReader.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
byte[] privateKeyBytes = pemObject.getContent();
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(x509KeySpec);
For RSA private keys you should be using PKCS8EncodedKeySpec if your key is encoded in PKCS8 format. PKCS8 format usually looks like :
-----BEGIN PRIVATE KEY-----
base64 encoded der key
-----END PRIVATE KEY-----
If your key is in PKCS1 format and looks like :
-----BEGIN RSA RIVATE KEY-----
base64 der encoded key
-----END RSA PRIVATE KEY-----
you should first convert it to PKCS8 format and then use the class mentioned above.
However doing same for public key doesn't throw exception and works - why?
Because public keys, which are usually part of Certificates, are encoded in X509 format, however private keys are usually encoded in PKCS format.

is PKCS8EncodedKeySpec capable to read both PKCS1 and PKCS8 private key

Although the title is a question but I just managed to do this with the same code, while I am just not sure whether PKCS8EncodedKeySpec is designed to do this job, or my private key is generated by accident to make this work(key generated by openssl).
my code(pemPath is pem type private key file path):
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(pemPath))) {
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
if (!line.startsWith("--")) {
stringBuilder.append(line);
}
}
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
Base64.getDecoder().decode(stringBuilder.toString()));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey key = keyFactory.generatePrivate(priPKCS8);
}
And also I paste PKCS1 file content:
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAoRVeLT5PrbQmFbmbc1zLFJuz5Sx3yTc9IIem13/+gNcrdqqa
QVd/2noPmIm1xUkdQfvRm72k0Mm1s78bcadTsZt4XL2OvAKBA0hyox2re/PLLp05
NP2WKFyF+O2OteZhzQHPxQFBn/Nt1pYbYctV7bbw4RsdQXXz72TY+WBfIg6m/LeG
uPG/4UM5IAw9G2IEOkA/V00rsjOLzRvHyqu473zaFNbaObN9rnakCUFeU07L3Eeo
EBQoZPiIyTAJ8eN/EnaudLrvErOog2E4LcpAOpoUDBdi2XYT0iE0stDoj62zP+gL
ubZQKV81N0qk1KehxeLcHtpXtvlWaITnmIovVQIDAQABAoIBAFw/W2Z3h7PHNgQ8
/cvr+RS9MiFs/NBLhQMLDD3fO8VW87oKazo+PKbGPgYx3tmzTEEMT/ZrfWZgDWF5
cOL+WwycJyqdF4+Ns4pIw/WruRfL/rCcMzWof4Rvr/1lNdPvslRv1CdS190cz+sS
nW28v2HZ4UpYd8GUJfUTgLoXOYPLjYEQx8wri5tOPvAJuCFHCdpx59Fu0xaQP+Uz
3+CSAqdsBL4UKoz0lQ27128BEg/2a6kqhGvC/QHu+Mn0N4wbto8ndfpF8noSAhx2
s38q/E9gVYxKjA1g2yl74irPsPWT+gq97nQJ4jYRog2W4I+KYYDAL9xP3ux+kGis
6h1GWcECgYEA1Bh/EgwlamT+y5oJhZgYEQAFtCZEOjyjWuJRwYTuaKrTJjudk0xZ
KPsGy8+Wi3QYsGUowAsQNws+3hzD1dIqWO92CXgkaJ1iZ3mVMsR+xgdb67jFrzZM
PerqXK3wOlaDe/Mikfyqcp9KERuSVKi/XMv7G/1ZaYk4/SPElqcXy1ECgYEAwm2Z
1Wy/E0uQ8Aj7x1nujCUQjvmTWhkU2aHW/1+STjERfXv5XdaR2N6tFSpWRRHAezHG
5b1NmskNKHCtYAdWFuZC2TXUoCVpnhZnS5gAf1H+konKjeknlLgYIZDB59hzMmCE
uOGEHk/rtZQuB4nR0jYk1eE14IOCQe6j73EsmsUCgYEAoINBd6kVj+WLJw8WcbJf
VE5OAr6LF//YKOnqsagmvbO6RM8NO/Kl72u7AvfBmRxtquGD64Lz7JRoKbRfSSza
u5/OFvCDrWCenmzXSyqGPtxG5qhZg3Z2UM2+PBGvIpdivqWM3ir32/NNHJFfNw7W
dCYzjkU4K7ScGoe9Ob6PNWECgYBrm514sidO1pPxRgeT3gJZLmprt1YGl7sieKA1
1v9yp1W2zdbGSOuPzC6feD8nGxwdEv+yR4OcF4pgx3wtmeHb6rU04sY7nHlINb4V
6Jrd3NO45upkgJaXiQxSB3xKhVG8oA7VBHvLCxC5/rk7Etw+LBlXBr569E4zj5DM
BSdawQKBgQC55RdlSLarKyEF9bMVPQDQ0Qko6pS+pw2kmg2CMXhk7eBsTUAMuVh4
/FNu3h6QiGpHstV1NRmF9oPRTltd8Mh3aCy8JAOY9/hZ7NScxKy2j91jQl+geSOC
FPSvfPlyMwPDvR3urCQKYq3NMJ4jWqcle4YX0w/rG5yY05gpQNVWiQ==
-----END RSA PRIVATE KEY-----
and PKCS8 file content:
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQChFV4tPk+ttCYV
uZtzXMsUm7PlLHfJNz0gh6bXf/6A1yt2qppBV3/aeg+YibXFSR1B+9GbvaTQybWz
vxtxp1Oxm3hcvY68AoEDSHKjHat788sunTk0/ZYoXIX47Y615mHNAc/FAUGf823W
lhthy1XttvDhGx1BdfPvZNj5YF8iDqb8t4a48b/hQzkgDD0bYgQ6QD9XTSuyM4vN
G8fKq7jvfNoU1to5s32udqQJQV5TTsvcR6gQFChk+IjJMAnx438Sdq50uu8Ss6iD
YTgtykA6mhQMF2LZdhPSITSy0OiPrbM/6Au5tlApXzU3SqTUp6HF4twe2le2+VZo
hOeYii9VAgMBAAECggEAXD9bZneHs8c2BDz9y+v5FL0yIWz80EuFAwsMPd87xVbz
ugprOj48psY+BjHe2bNMQQxP9mt9ZmANYXlw4v5bDJwnKp0Xj42zikjD9au5F8v+
sJwzNah/hG+v/WU10++yVG/UJ1LX3RzP6xKdbby/YdnhSlh3wZQl9ROAuhc5g8uN
gRDHzCuLm04+8Am4IUcJ2nHn0W7TFpA/5TPf4JICp2wEvhQqjPSVDbvXbwESD/Zr
qSqEa8L9Ae74yfQ3jBu2jyd1+kXyehICHHazfyr8T2BVjEqMDWDbKXviKs+w9ZP6
Cr3udAniNhGiDZbgj4phgMAv3E/e7H6QaKzqHUZZwQKBgQDUGH8SDCVqZP7LmgmF
mBgRAAW0JkQ6PKNa4lHBhO5oqtMmO52TTFko+wbLz5aLdBiwZSjACxA3Cz7eHMPV
0ipY73YJeCRonWJneZUyxH7GB1vruMWvNkw96upcrfA6VoN78yKR/Kpyn0oRG5JU
qL9cy/sb/VlpiTj9I8SWpxfLUQKBgQDCbZnVbL8TS5DwCPvHWe6MJRCO+ZNaGRTZ
odb/X5JOMRF9e/ld1pHY3q0VKlZFEcB7McblvU2ayQ0ocK1gB1YW5kLZNdSgJWme
FmdLmAB/Uf6SicqN6SeUuBghkMHn2HMyYIS44YQeT+u1lC4HidHSNiTV4TXgg4JB
7qPvcSyaxQKBgQCgg0F3qRWP5YsnDxZxsl9UTk4CvosX/9go6eqxqCa9s7pEzw07
8qXva7sC98GZHG2q4YPrgvPslGgptF9JLNq7n84W8IOtYJ6ebNdLKoY+3EbmqFmD
dnZQzb48Ea8il2K+pYzeKvfb800ckV83DtZ0JjOORTgrtJwah705vo81YQKBgGub
nXiyJ07Wk/FGB5PeAlkuamu3VgaXuyJ4oDXW/3KnVbbN1sZI64/MLp94PycbHB0S
/7JHg5wXimDHfC2Z4dvqtTTixjuceUg1vhXomt3c07jm6mSAlpeJDFIHfEqFUbyg
DtUEe8sLELn+uTsS3D4sGVcGvnr0TjOPkMwFJ1rBAoGBALnlF2VItqsrIQX1sxU9
ANDRCSjqlL6nDaSaDYIxeGTt4GxNQAy5WHj8U27eHpCIakey1XU1GYX2g9FOW13w
yHdoLLwkA5j3+Fns1JzErLaP3WNCX6B5I4IU9K98+XIzA8O9He6sJApirc0wniNa
pyV7hhfTD+sbnJjTmClA1VaJ
-----END PRIVATE KEY-----
By the way, both private keys succeed to decrypt the same cipher text.
And the openssl command:
openssl genrsa -out rsa_private_key_2048.pem 2048
openssl pkcs8 -topk8 -in rsa_private_key_2048.pem -out pkcs8_rsa_private_key_2048.pem -nocrypt
What if the private key in PEM format(PKCS#1 format)string being passed in a json payload input. PKCS8EncodedKeySpec is currently working for both PKCS#1 and PKCS#8. The provider is SunRsaSign.

BouncyCastle Encoding differences

I'm trying to calculate the hash of a public key to match what ApplePay is giving me. I can't get BouncyCastle in C# to give me the public key in the right format so I can hash it. However the Java version that I'm porting works just fine. OpenSSL also gives the right response, but I'd prefer not to have to deal with OpenSSL as well as BC to do this.
Java: (works)
ECPublicKey key = (ECPublicKey) certificate.getPublicKey();
byte[] keyBytes = key.getEncoded();
MessageDigest messageDigest = MessageDigest.getInstance("SHA256", PROVIDER_NAME);
byte[] keyHash = messageDigest.digest(keyBytes);
String base64keyBytes = new String(Base64.encode(keyBytes));
//base64keyBytes == MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+4wQWWRnPqGlsncZX17t0CfLOl6u
// 68aXUsqnzlIcpCdDukHhxibd2MjHPFGpnK3ZKdHxIFh+NBQvGssM5ncm1g==
// line break added for readability
OpenSSL: (works)
openssl x509 -noout -pubkey -in <file> -inform der
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+4wQWWRnPqGlsncZX17t0CfLOl6u
68aXUsqnzlIcpCdDukHhxibd2MjHPFGpnK3ZKdHxIFh+NBQvGssM5ncm1g==
-----END PUBLIC KEY-----
C#: (keyBytes are wrong)
SubjectPublicKeyInfo pubinfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(certificate.GetPublicKey());
byte[] keyBytes = pubinfo.GetEncoded();
byte[] keyHash = DigestUtilities.CalculateDigest("SHA_256", keyBytes);
string keyBytesEncoded = Convert.ToBase64String(keyBytes);
/*keyBytesEncoded == MIIBKjCB4wYHKoZIzj0CATCB1wIBATAsBgcqhkjOPQEBAiEA/////wAAAAEAAA
AAAAAAAAAAAAD///////////////8wWwQg/////wAAAAEAAAAAAAAAAAAAAAD/
//////////////wEIFrGNdiqOpPns+u9VXaYhrxlHQawzFOw9jvOPD4n0mBLAx
UAxJ02CIbnBJNqZnjhE50mt4GffpAEIQNrF9Hy4SxCR/i85uVjpEDydwN9gS3r
M6D0oTlF2JjClgIhAP////8AAAAA//////////+85vqtpxeehPO5ysL8YyVRAg
EBA0IABPuMEFlkZz6hpbJ3GV9e7dAnyzperuvGl1LKp85SHKQnQ7pB4cYm3djI
xzxRqZyt2SnR8SBYfjQULxrLDOZ3JtY=
line break added for readability */
What is the right way to port the GetEncoding?
I know it's the key data that wrong because when I manually force in the data from openssl I get the right hash.
EDIT: Added the out puts.
I think it would just be:
byte[] keyBytes = certificate.CertificateStructure.SubjectPublicKeyInfo.GetDerEncoded()

RSA decryption using modulus and exponent

My task: I have encrypted (RSA) data and public key as modulus and exponent. I have to write decryption code.
My problem with it: My implementation doesn't work ;) As far as I know philosophy is simple "open text" == rsa(public_key, rsa(private_key, "open text")) Edit: Exactly my assumption was wrong (Assumption is mother of all fu..ups ;) ). It should be "open text" == rsa(private_key, rsa(public_key, "open text")) because in RSA, public key is used for encryption and private for decryption.
I assumed that I can have public key which doesn't correspond to private key using during encryption so for tests I created own keys in such way:
openssl genrsa -des3 -out server.key 1024
openssl req -new -key server.key -out server.csr
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
I got public key modulus and exponent using command:
openssl x509 -in server.crt -text
For encryption testing I'm using code
//Reads private key from file
//StringPasswordFinder is my tmp implementation of PasswordFinder
PEMReader pemReader = new PEMReader(new FileReader("/path/to/server.key"), new StringPasswordFinder());
KeyPair keyPair = (KeyPair) pemReader.readObject();
PrivateKey pk = keyPair.getPrivate();
//text for encryption
String openText = "openText";
//encryption
Cipher rsaCipher = Cipher.getInstance("RSA", "BC");
rsaCipher.init(Cipher.ENCRYPT_MODE, pk);
byte[] encrypted = rsaCipher.doFinal(openText.getBytes("utf-8"));
And for decryption of encrypted text I use code
//modulus hex got using openssl
byte[] modulus = Hex.decodeHex("very long hex".toCharArray());
//exponent hex got using openssl
byte[] exponent = Hex.decodeHex("010001".toCharArray());
//initialization of rsa decryption engine
RSAEngine rsaEngine = new RSAEngine();
rsaEngine.init(false, new RSAKeyParameters(false, new BigInteger(modulus), new BigInteger(exponent)));
//input - encrypted stream
ByteArrayInputStream bais = new ByteArrayInputStream(encrypted);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//reading blocks from the input stream and decrypting them
int bytesRead = 0;
byte[] block = new byte[rsaEngine.getInputBlockSize()];
while ((bytesRead = bais.read(block)) > -1) {
baos.write(rsaEngine.processBlock(block, 0, bytesRead));
}
//dispalying decrypted text
System.out.println(new String(baos.toByteArray(), "utf-8"));
And after all displayed text is not. Can anybody show me where I'm wrong?
Edit: Summing up this problem has no solution. Because it's not possible encrypt message using private key and later decrypt it using public one. At general I mixed up encryption with signing message and decryption with verification. Because during making signature private key is used and public is used during verification. Btw, MByD thx for important clue.
I am not so familiar with java libraries for RSA, the times I tried to implement RSA in java was to build all calculations by myself, but if I understood you correct, I see 2 problems:
the data should be encrypted with the public key and decrypted with private key, not the other way around (since everyone with public key will be able to decrypt it...)
the public key should match the private key, otherwise, anyone with any private key will be able to decrypt data encrypted with any public key...
Also, for very long data, you should not use public key encryption. Instead, encrypt the data in some other algorithm (RC4, AES, etc.) and encrypt the key in RSA (similar to PGP approach)

Categories

Resources