com.subgraph.orchid.crypto.RSAKeyEncoder Maven / Gradle / Ivy
package com.subgraph.orchid.crypto;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.List;
import com.subgraph.orchid.crypto.ASN1Parser.ASN1BitString;
import com.subgraph.orchid.crypto.ASN1Parser.ASN1Integer;
import com.subgraph.orchid.crypto.ASN1Parser.ASN1Object;
import com.subgraph.orchid.crypto.ASN1Parser.ASN1Sequence;
import com.subgraph.orchid.encoders.Base64;
public class RSAKeyEncoder {
private final static String HEADER = "-----BEGIN RSA PUBLIC KEY-----";
private final static String FOOTER = "-----END RSA PUBLIC KEY-----";
private final ASN1Parser asn1Parser = new ASN1Parser();
/**
* Parse a PKCS1 PEM encoded RSA public key into the modulus/exponent components
* and construct a new RSAPublicKey
*
* @param pem The PEM encoded string to parse.
* @return a new RSAPublicKey
*
* @throws GeneralSecurityException If an error occurs while parsing the pem argument or creating the RSA key.
*/
public RSAPublicKey parsePEMPublicKey(String pem) throws GeneralSecurityException {
try {
byte[] bs = decodeAsciiArmoredPEM(pem);
ByteBuffer data = ByteBuffer.wrap(bs);
final ASN1Object ob = asn1Parser.parseASN1(data);
final List seq = asn1ObjectToSequence(ob, 2);
final BigInteger modulus = asn1ObjectToBigInt(seq.get(0));
final BigInteger exponent = asn1ObjectToBigInt(seq.get(1));
return createKeyFromModulusAndExponent(modulus, exponent);
} catch (IllegalArgumentException e) {
throw new InvalidKeyException();
}
}
private RSAPublicKey createKeyFromModulusAndExponent(BigInteger modulus, BigInteger exponent) throws GeneralSecurityException {
RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
KeyFactory fac = KeyFactory.getInstance("RSA");
return (RSAPublicKey) fac.generatePublic(spec);
}
/**
* Return the PKCS1 encoded representation of the specified RSAPublicKey. Since
* the primary encoding format for RSA public keys is X.509 SubjectPublicKeyInfo,
* this needs to be converted to PKCS1 by extracting the needed field.
*
* @param publicKey The RSA public key to encode.
* @return The PKCS1 encoded representation of the publicKey argument
*/
public byte[] getPKCS1Encoded(RSAPublicKey publicKey) {
return extractPKCS1KeyFromSubjectPublicKeyInfo(publicKey.getEncoded());
}
/*
* SubjectPublicKeyInfo encoding looks like this:
*
* SEQUENCE {
* SEQUENCE {
* OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
* NULL
* }
* BIT STRING (encapsulating) { <-- contains PKCS1 encoded key
* SEQUENCE {
* INTEGER (modulus)
* INTEGER (exponent)
* }
* }
* }
*
* See: http://www.jensign.com/JavaScience/dotnet/JKeyNet/index.html
*/
private byte[] extractPKCS1KeyFromSubjectPublicKeyInfo(byte[] input) {
final ASN1Object ob = asn1Parser.parseASN1(ByteBuffer.wrap(input));
final List seq = asn1ObjectToSequence(ob, 2);
return asn1ObjectToBitString(seq.get(1));
}
private BigInteger asn1ObjectToBigInt(ASN1Object ob) {
if(!(ob instanceof ASN1Integer)) {
throw new IllegalArgumentException();
}
final ASN1Integer n = (ASN1Integer) ob;
return n.getValue();
}
private List asn1ObjectToSequence(ASN1Object ob, int expectedSize) {
if(ob instanceof ASN1Sequence) {
final ASN1Sequence seq = (ASN1Sequence) ob;
if(seq.getItems().size() != expectedSize) {
throw new IllegalArgumentException();
}
return seq.getItems();
}
throw new IllegalArgumentException();
}
private byte[] asn1ObjectToBitString(ASN1Object ob) {
if(!(ob instanceof ASN1BitString)) {
throw new IllegalArgumentException();
}
final ASN1BitString bitstring = (ASN1BitString) ob;
return bitstring.getBytes();
}
private byte[] decodeAsciiArmoredPEM(String pem) {
final String trimmed = removeDelimiters(pem);
return Base64.decode(trimmed);
}
private String removeDelimiters(String pem) {
final int headerIdx = pem.indexOf(HEADER);
final int footerIdx = pem.indexOf(FOOTER);
if(headerIdx == -1 || footerIdx == -1 || footerIdx <= headerIdx) {
throw new IllegalArgumentException("PEM object not formatted with expected header and footer");
}
return pem.substring(headerIdx + HEADER.length(), footerIdx);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy