BouncyCastle Encoding differences - java

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()

Related

How do you convert RSA private key to openssh format in Java?

I need to generate an openssh format key pair in java.
I generate a public private key pair using the KeyPairGenerator in java and save it to a file:
gen.initialize(2048);
KeyPair pair = gen.generateKeyPair();
Base64.Encoder encoder = Base64.getEncoder();
PrivateKey privateKey = pair.getPrivate();
PublicKey publicKey = pair.getPublic();
System.out.println(encoder.encodeToString(publicKey.getEncoded());
System.out.println(encoder.encodeToString(privateKey.getEncoded());
However, if I try to use this with ssh-keygen I get a "Failed to load key private.key: invalid format"
How can I either convert the PKCS#8 private key to openssh format or generate a key pair in the openssh format?
EDIT:
To clarify, I am looking for something like this:
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAo+2pHTLFxKmzyMZC4VVnrFTTHONdoKRYPeT4+ohK/g2X7U8aBj1V
FyQntFUMIRHgQcZcGQX0tLpnv2J5Fyr1YfxCakLB6W5XIAHIwuSRO4H6YpX4iMW7C7RWsq
Q2JUW5Vab89WduAotltIjLOyRUO2E8LWtOcCnnk1j+tVaB6q+EvK0MNxU5JOtybRkQWJTa
ONkBuZMeJ4e2Et/WVNceY6ZmBlMEVVW9uQ4j0MQk3UoWkY4JdPinIGi1Do2xUfuTwbmE0w
y4yb5yMw8fZvVDxFqNJSNLHFUihzsRMgbDRQ+P30e9uFKBBJ0pe2ZArYWLA40ojPzzi2ob
oB+NwSVHDQAAA7iTErFrkxKxawAAAAdzc2gtcnNhAAABAQCj7akdMsXEqbPIxkLhVWesVN
Mc412gpFg95Pj6iEr+DZftTxoGPVUXJCe0VQwhEeBBxlwZBfS0ume/YnkXKvVh/EJqQsHp
blcgAcjC5JE7gfpilfiIxbsLtFaypDYlRblVpvz1Z24Ci2W0iMs7JFQ7YTwta05wKeeTWP
61VoHqr4S8rQw3FTkk63JtGRBYlNo42QG5kx4nh7YS39ZU1x5jpmYGUwRVVb25DiPQxCTd
ShaRjgl0+KcgaLUOjbFR+5PBuYTTDLjJvnIzDx9m9UPEWo0lI0scVSKHOxEyBsNFD4/fR7
24UoEEnSl7ZkCthYsDjSiM/POLahugH43BJUcNAAAAAwEAAQAAAQEAn+Qoxn0GX4sy+8s9
4rG93F4kSJIQeaazFzPmEd+sXd5+aI52EM3z2A2A2Kj3mq3n8d/7ZsDjbQBAP3FaMNnK3B
cD5MdWgkwImQSEgGwWqFdgFJa5AxbyGTl+MuJuma5HVp75LpgCumKjAhNHP1lw+zYdTyPS
Lx8AbD0qu080iuWtMwWV5Hap9ZHjYEVIrgDArxbzTT0wvpqT1cFCon9vJVJpPOWsRbpEgt
d+M+eOpBL7mDKJhub9soguhhRxqgYvgigJueeqMnYpxPye7oewratTMAWskk0n0zXKRON0
q9zVVfdc9Md0cqkkbNGyQ/VdbrM+zZt+46v0zgyk6zdrdQAAAIAcnNjD6Pm8YFXa2mkI47
m+2SRcKJ4jmvGJTN8iRan7nmBMNDfC4EbGW8eQbcl4PsUevqd0O2PUpDNL5m9stAF8vBna
QRlzlWNncdJTBS4Rlzf+LisCrcecdl+Vg1tSLpur/+prk9VFZDnj5uUPSHb3zdLfYt7onV
VA0FSm0PeFdwAAAIEA/YQw567hHMp2mqaxW3ogNsqSv+nRiZEPqEHvCW3znVMJZPsbubpg
MiJaQIfi1fNkgPu3pKdCu1M1+tf4hKK5Tjh8jPh9KSi/T6VZ4lQbFZaMFWXeY0FKKszkGR
b+8BHHdH9a8XnAJvwWOGN5evJl69j7VylMB0ZIIZHiEt/ZDIsAAACBAKWIyVHk+RLz/maM
11lajRrnM46YXRJR4pm0AWQLcso74Q5DMhi0I6joUyrTPliUKcWM38lCN3uKV5qBioCzJv
PfWgDquQCj7kMCC98CA3YSbffeLtMigon+kT3JZb9tYKbTyRx35yw+6ghbi7ccWZgLoz6w
g6ulyyuhmYLHM3XHAAAAAAEC
-----END OPENSSH PRIVATE KEY-----
versus this:
-----BEGIN RSA PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCj7akdMsXEqbPI
xkLhVWesVNMc412gpFg95Pj6iEr+DZftTxoGPVUXJCe0VQwhEeBBxlwZBfS0ume/
YnkXKvVh/EJqQsHpblcgAcjC5JE7gfpilfiIxbsLtFaypDYlRblVpvz1Z24Ci2W0
iMs7JFQ7YTwta05wKeeTWP61VoHqr4S8rQw3FTkk63JtGRBYlNo42QG5kx4nh7YS
39ZU1x5jpmYGUwRVVb25DiPQxCTdShaRjgl0+KcgaLUOjbFR+5PBuYTTDLjJvnIz
Dx9m9UPEWo0lI0scVSKHOxEyBsNFD4/fR724UoEEnSl7ZkCthYsDjSiM/POLahug
H43BJUcNAgMBAAECggEBAJ/kKMZ9Bl+LMvvLPeKxvdxeJEiSEHmmsxcz5hHfrF3e
fmiOdhDN89gNgNio95qt5/Hf+2bA420AQD9xWjDZytwXA+THVoJMCJkEhIBsFqhX
YBSWuQMW8hk5fjLibpmuR1ae+S6YArpiowITRz9ZcPs2HU8j0i8fAGw9KrtPNIrl
rTMFleR2qfWR42BFSK4AwK8W8009ML6ak9XBQqJ/byVSaTzlrEW6RILXfjPnjqQS
+5gyiYbm/bKILoYUcaoGL4IoCbnnqjJ2KcT8nu6HsK2rUzAFrJJNJ9M1ykTjdKvc
1VX3XPTHdHKpJGzRskP1XW6zPs2bfuOr9M4MpOs3a3UCgYEA/YQw567hHMp2mqax
W3ogNsqSv+nRiZEPqEHvCW3znVMJZPsbubpgMiJaQIfi1fNkgPu3pKdCu1M1+tf4
hKK5Tjh8jPh9KSi/T6VZ4lQbFZaMFWXeY0FKKszkGRb+8BHHdH9a8XnAJvwWOGN5
evJl69j7VylMB0ZIIZHiEt/ZDIsCgYEApYjJUeT5EvP+ZozXWVqNGuczjphdElHi
mbQBZAtyyjvhDkMyGLQjqOhTKtM+WJQpxYzfyUI3e4pXmoGKgLMm899aAOq5AKPu
QwIL3wIDdhJt994u0yKCif6RPcllv21gptPJHHfnLD7qCFuLtxxZmAujPrCDq6XL
K6GZgsczdccCgYEAu1VvfgRwPIIv2l/LNzmrjFFs13vEZs9WrpLqPCGPn3W4v3H/
LuNWKjXQU1cWe9r7LYTUU0t1uE7o2I+3COvl2rNK9jC47C08EXKyVNipmu5AXZ+F
Efsw/yegdhnUETpSFPf3D/FT2Hr3QHvhTMTKI9mXAPV3RPjeXFAcqq3XCbkCgYBI
2scgUBJ/kPuqztoI7Z2k9ZTvcYelBH0jAOKL0a4X6/rFeDWYQdBgCsBv3MFX4v3v
gG0N+yLIML8VtWXr9u1x8B+Av83kxkGbJE9tO0miscHMkfEx48JoUa5C71zkv5MG
Wbft26fXBWmjfAcl9EhKbvTOJooNBc9ByMHzmRDBCQKBgByc2MPo+bxgVdraaQjj
ub7ZJFwoniOa8YlM3yJFqfueYEw0N8LgRsZbx5BtyXg+xR6+p3Q7Y9SkM0vmb2y0
AXy8GdpBGXOVY2dx0lMFLhGXN/4uKwKtx5x2X5WDW1Ium6v/6muT1UVkOePm5Q9I
dvfN0t9i3uidVUDQVKbQ94V3
-----END RSA PRIVATE KEY-----
For the private key, PKCS8 PEM is compatible with OpenSSH. (Contrary to Maarten you can't use DER, but contrary to Topaco you aren't required to convert to PKCS1 'traditional' format.) You do need to create correct PEM, which requires header and footer lines AND linebreaks in the body (not a single humongous string); see wikipedia or rfc7468. Java 8+ Base64.getMimeEncoder() does the linebreaks.
PrivateKey pkey = pair.getPrivate();
System.out.println ("-----BEGIN PRIVATE KEY-----");
System.out.println (Base64.getMimeEncoder().encodeToString(pkey.getEncoded()));
// to be strict use getMimeEncoder(64, value_of_line_separator)
// for PEM, but in practice MIME default 76 actually works
System.out.println ("-----END PRIVATE KEY-----");
Note this format is unencrypted, thus anyone who gets/sees your file (or a copy or backup of it) gets your private key. Whether, when and how this is a problem depends on many factors and belongs on security.SX not SO. OpenSSH supports and generally encourages encrypted privatekey files, but (also) supports unencrypted ones.
Public key is harder. OpenSSH's public key format does not follow the standards supported by Java (without adding BouncyCastle). However, you can construct this by hand, and this has been answered several times; see my list at ssh-keygen and openssl gives two different public keys and example in How to Calculate Fingerprint From SSH RSA Public Key in Java? (which references the same). Note the X.509 SubjectPublicKeyInfo format preferred by OpenSSL is the same format used by Java's PublicKey.getEncoded() (which Java's .getFormat() ambiguously calls "X.509"), except that Java uses binary/DER only while OpenSSL uses both binary/DER and PEM.
Update: your complaint was 'fails to load in ssh-keygen' so I thought you could use any format accepted by OpenSSH, which the PKCS8 format above is. If you insist on the OpenSSH-defined 'new' format, even though it isn't necessary for OpenSSH including ssh-keygen, that's much harder in plain Java without BouncyCastle. So far I've got the case you now show (RSA unencrypted) only:
RSAPrivateCrtKey pkey = // generated or read (I used test data I already have)
byte[] alg = "ssh-rsa".getBytes(), none = "none".getBytes();
byte[] nbyt = pkey.getModulus().toByteArray(), ebyt = pkey.getPublicExponent().toByteArray();
int rand = new Random().nextInt();
ByteBuffer pub = ByteBuffer.allocate(nbyt.length+50); // always enough, but not too much over
for( byte[] x : new byte[][]{alg,ebyt,nbyt} )
{ pub.putInt(x.length); pub.put(x); }
ByteBuffer prv = ByteBuffer.allocate(nbyt.length*4+50); // ditto
prv.putInt(rand); prv.putInt(rand);
for( byte[] x : new byte[][]{alg,nbyt,ebyt,pkey.getPrivateExponent().toByteArray(),
pkey.getCrtCoefficient().toByteArray(),pkey.getPrimeP().toByteArray(),pkey.getPrimeQ().toByteArray()} )
{ prv.putInt(x.length); prv.put(x); }
prv.putInt(0); // no comment
for( int i = 0; prv.position()%8 != 0; ) prv.put((byte)++i); // 8 apparently default? IDK
ByteBuffer all = ByteBuffer.allocate(100+pub.position()+prv.position()); // ditto
all.put("openssh-key-v1".getBytes()); all.put((byte)0);
all.putInt(none.length); all.put(none); // cipher
all.putInt(none.length); all.put(none); // pbkdf
all.putInt(0); all.putInt(1); // parms, count
all.putInt(pub.position()); all.put(pub.array(),0,pub.position());
all.putInt(prv.position()); all.put(prv.array(),0,prv.position());
byte[] result = Arrays.copyOf(all.array(), all.position());
System.out.print ("-----BEGIN OPENSSH PRIVATE KEY-----\n"
+ Base64.getMimeEncoder(68,"\n".getBytes()).encodeToString(result)
+ "\n-----END OPENSSH PRIVATE KEY-----\n");
// MimeEncoder only does multiple of 4 char, consistent with the 3-to-4 logic
// OpenSSH writes 70 but accepts 68; it requires no-CR (contrary to MIME)

Generate JSON web token (JWT) with a private key

I'm trying to generate JWT but I'm receiving this error:
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout private.key -out certificate_pub.crt
I'm using the io.jsonwebtoken.Jwts library, and a private key in string form but I'm getting errors.
Map<String, Object> payload = new HashMap<>();
payload.put("iss", orgId);
payload.put("sub", accountId);
payload.put("exp", expirationTime);
payload.put("aud", new
StringBuilder("Url").append("/c/").append(apiKey).toString());
payload.put(new StringBuilder("Url").append("/s/entt_sdk").toString(), Boolean.TRUE);
return Jwts.builder().setClaims(payload).**signWith**(SignatureAlgorithm.RS256, privateKeyStr).compact();
java.lang.IllegalArgumentException: Base64-encoded key bytes may only be specified for HMAC signatures. If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.
My private key looks like this:
-----BEGIN PRIVATE KEY-----
sajdkjsadkjsahdkjsadksadkjsadkjs
-----END PRIVATE KEY-----
The error is quite straightforward - this method can be used only for Hmac algorihtms. For RSA based algorithms you will have to use signWith(SignatureAlgorithm, Key).
The key that I will use will not be in PEM format - it will be base64 DER encoded String - to do it I had to get rid of the beginning and ending of PEM format for this key - -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- :
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC9/xBGBRhpSBa/
Xc/5CeAQjjVMcXfIFOqeIc/kq7dnsLz+ntTrvTE3fyz0E8J8MLMTNypK7irt8MB0
0Gpm/NJHnLSGqgSqIXAvUbUsxCO8ULSTKLTQs4RiDbUtOWZgacvRN0IYRnN2tDWf
DWTYAElBSValTt/jvHw72BMcLXd8plGQYYD52l6+w/7ENgLG+hNbVewdM/saJKyL
Y/jhRl758XWGw5bCmIYk9BCbUVDLc5PN+iiYoBFTQYwP0Y1ln58vNZ/CDBw2mW3q
TyploxoAdmG86km4EK2mtlhWBaUshfHORxGkWlCXcwazWDoc5QJ24McaYbcQOcPW
lLDgP8MvAgMBAAECggEAHDX6MZtiE4fbsNB6J+06ctrauR4D/hZ0+8PjfX2tvty0
Q05MKTCvVSEyCI/CifQlMs43HmccwrXDrdSgZ+hURMPU3kXyaVyLrssADsSU1cpZ
9ZvOtUpidri4VR23wMsUs1z0GGylilOZvqMbfSMVvXbpQaRjhAohnDUqKT3rBvvd
fqO8KFl8FCgMEbbPvym5tJvrYfe9WalisnrGrrCZoaBmR5dEbjUfWrMg6bMcfPlz
rVaney8+UdFu15RUXTno3mu+glIYz1MyYk6LdlgBrb19gBscykqi0wYhZ312Yk0B
SXx+RIi49oZy7IU4jybzOSqWL79L4rQdvtBrp/dx6QKBgQDn/XeuCATwLdFyeo4/
ksVlhXIp9ykAZSFk/wnapcsvLSV59edI8mkslAwTTbgqPn0hvxVdwf0k23wipkGl
FiujHMCGoeT9ZwYs9uDEkGABATXomr7eC64AEfuUnUZMj3s0BgG5S/mGonlkIlTt
RpvxzMeYnRvLjDXZMP7FKCz0DQKBgQDRqPq+w7MQFBaBMTE7+685QeR+xxGnMipW
Qf579E++ihslGx6LztQnFhbah2VEVCPBq7R/BiEHiW4bA+DiTC8HnMsZi3jhlO9q
yw0DSZUSX8vsgNW2ghJOF9JnZEbptN3RlD11koSvkFZiUuxHYa0n6ti38CwfLxgV
MCuL5XOZKwKBgGi4CqD9L7V3CTdiyPk7eG1mOm1lCxYJkHR1h24yLrCB8YvHC3rr
Kbycq4K/L2WqRXPJPIzQ90L+7F77q2AozNPZM7LSO3qDWc9MNZOlFCD/+eSgjY3P
ueCAPY8NG2GN1vBZ0cdh2yYCC0e/E5TzrYsNg/+I07Yi+V+r9STsCLa1AoGAJnJo
WOcmRQKKBfLxZmCHB2bv8dergw+N9/duJWjt3rEQvUM13Ml22hwQ4M4HYfpT/EXy
eYC0Od+X01houtbhoPG9xNdwuV1Icjr+DeZGcfIjQSF3D1rW5H811EPtRRonuzEF
/DN8JX3AeZNfRM/CoxlL2J8wWB+YuPn2YlcXVbUCgYEAmVETM7+OBW9YKtv6zvKe
OZeZUIDIUZDqZgLd3IT7rikVCedIljWNhroXU1wNMssJPkfiQToGaykUMbBcgZKI
neU2IuYWaLXBN9oAj1u7/YQ0DpPqk/Sb2FpVX5eKfp4cu8XdyezxNuFFsPVdGBhB
xhqJOJuUc/ZKbo5Stc3NXEE=
Here is example of how to read this key and sign JWT with it :
//create payload
Map<String, Object> payload = new HashMap<>();
payload.put("iss", "orgId");
payload.put("sub", "orgId");
payload.put("exp", "orgId");
payload.put("aud", new
StringBuilder("Url").append("/c/").append("key").toString());
payload.put(new StringBuilder("Url").append("/s/entt_sdk").toString(), Boolean.TRUE);
// read key
String privateKeyB64 = Files.lines(Paths.get("src/main/resources/private.key")).collect(Collectors.joining());
byte[] privateKeyDecoded = Base64.getDecoder()
.decode(privateKeyB64);
//create key spec
PKCS8EncodedKeySpec spec =
new PKCS8EncodedKeySpec(privateKeyDecoded);
// create key form spec
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(spec);
//create signed JWT - JWS
String jws = Jwts.builder().setClaims(payload).signWith(SignatureAlgorithm.RS256, privateKey).compact();
System.out.println(jws);
Notice I used PKCS8EncodedKeySpec because your key seems to be in PKCS8 format. The output is :
eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJvcmdJZCIsImF1ZCI6IlVybC9jL2tleSIsImlzcyI6Im9yZ0lkIiwiZXhwIjoib3JnSWQiLCJVcmwvcy9lbnR0X3NkayI6dHJ1ZX0.m8ASk4kUNx41csikpd0zALLQTjwG2pc0Ba0D9PGLVbI2NaY0IIXgaVVVJcIERz4ejj_jfq436v6v0_QnxdmvjMAnx88UmHGdrCT0V5MZl008LP4g4LrV-WczNltCUpoJQ-4CW6xkpXD03JIDQAYwaKb-PIOtm-pfLJhPPmxykc8QioueijhI5M__Pq5Nq0JCbkQxfGzxE5m_gJwwq7n290RBGRYH6AHeClaEJhDzLNitIejNvvua4zNNC6S1CHsa4ChaEFfRb9bi-jNEQW27IGhrKRCtuwleFwigl7oTIsyaRWlzuVNYcZHS707Z2o6Mkf9hDo8AGKURUVsJgA8WIg
I tested it on Java 8. For Java 11 I received an error with missing module regarding XML processing.

RSA - Java's KeyFactory fails where bouncycastle's PEM parser succeeds

I have a PEM formatted public key (received from an iOS device):
String pemPubKey = ""+
"-----BEGIN RSA PUBLIC KEY-----\n"+
"MIIBCgKCAQEA07ACQHTTrgX7ddNtyamh58xwD+S+pSrJz/Rah4zj0HIg4V/Ok5vk\n"+
"Wx6y4UyuKLCtefeiB2ipg/n1ZZ0eRac1B4UwPhAtILGQzgIUgOp0cQ3Cb94ugq92\n"+
"wxkxeEdWmIFIlXgWOf6I8yWp9DZaigrRhA2kPbY01zKxCsX1ZxKMVu2sU/HM1hJy\n"+
"aebLLND002yLzuRDLXbacmCt5U6vDQDjBmm3uZ26fEMF+GTCnn6fJBq5RDfRKjpS\n"+
"fVM0mCePO9RHiwu3oHfqoyLA2QGlCexXcIYq7KbJjC9vcamAWRqQdHlsSj5ezDTR\n"+
"GofA6HtQ+zNdGHOvqsYtbN8MJSlUXXy39wIDAQAB\n"+
"-----END RSA PUBLIC KEY-----";
If I try to parse it into a PublicKey using KeyFactory like that:
KeyFactory kf = KeyFactory.getInstance("RSA");
Pattern parse = Pattern.compile("(?m)(?s)^---*BEGIN.*---*$(.*)^---*END.*---*$.*");
String encoded = parse.matcher(pemPubKey).replaceFirst("$1");
byte[] pem = Base64.getMimeDecoder().decode(encoded);
PublicKey pubKey = kf.generatePublic(new X509EncodedKeySpec(pem));
I get: java.security.InvalidKeyException: IOException: algid parse error, not a sequence.
But when I use bouncycastle like that:
SubjectPublicKeyInfo subjectPublicKeyInfo =
(SubjectPublicKeyInfo) new PEMParser(new StringReader(pemPubKey)).readObject();
PublicKey pubKey;
if (PKCSObjectIdentifiers.rsaEncryption == subjectPublicKeyInfo.getAlgorithm().getAlgorithm()) {
DLSequence der = (DLSequence) subjectPublicKeyInfo.parsePublicKey().toASN1Primitive();
ASN1Object modulus, exponent;
modulus = (ASN1Object) der.getObjectAt(0);
exponent = (ASN1Object) der.getObjectAt(1);
RSAPublicKeySpec spec =
new RSAPublicKeySpec(new BigInteger(modulus.toString()), new BigInteger(exponent.toString()));
KeyFactory factory = KeyFactory.getInstance("RSA");
pubKey = factory.generatePublic(spec);
} else {
// Throw some exception
}
I get a valid PublicKey and the algorithm is identified correctly.
Why does java's parser fails here? And am I doing the migration from SubjectPublicKeyInfo to PublicKey correctly?
Update:
I've tried to verify the key using openssl:
$ openssl rsa -inform PEM -pubin -in pub.pem -text -noout
unable to load Public Key
140735659656136:error:0906D06C:PEM routines:PEM_read_bio:no start
line:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/pem/pem_lib.c:704:Expecting:
PUBLIC KEY
And after removing RSA from the header / footer:
$ openssl rsa -inform PEM -pubin -in pub.pem -text -noout unable to
load Public Key 140735659656136:error:0D0680A8:asn1 encoding
routines:ASN1_CHECK_TLEN:wrong
tag:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/asn1/tasn_dec.c:1164:
140735659656136:error:0D07803A:asn1 encoding
routines:ASN1_ITEM_EX_D2I:nested asn1
error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/asn1/tasn_dec.c:314:Type=X509_ALGOR
140735659656136:error:0D08303A:asn1 encoding
routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1
error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/asn1/tasn_dec.c:653:Field=algor,
Type=X509_PUBKEY 140735659656136:error:0906700D:PEM
routines:PEM_ASN1_read_bio:ASN1
lib:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/pem/pem_oth.c:84:
Java's parser didn't fail, your public key is not an instance of an encoded SubjectPublicKeyInfo structure that Java's X509EncodedKeySpec is expecting. I haven't gone through the Bouncycastle routines to see why it succeeded, but PEMParser is designed to parse many different kinds of so-called "PEM" files.
A SubjectPublicKeyInfo is defined in RFC 5280 as:
SubjectPublicKeyInfo ::= SEQUENCE {
algorithm AlgorithmIdentifier,
subjectPublicKey BIT STRING
}
Your public key is a simple PKCS#1 RSAPublicKey, defined in RFC 8017 as:
RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}
And just a final word about "PEM" files. You need to be careful when describing something as "PEM" format because PEM is not really a specific format. "PEM" is little more than encoding a true format in base64, then wrapping the base64 in "-----BEGIN -----" and "-----END -----" lines, where hopefully uniquely describes what the base64-encoded data is.

Read encrypted PKCS8 SpongyCastle 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

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