Package com.subgraph.orchid.crypto

Source Code of com.subgraph.orchid.crypto.RSAKeyEncoder

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<ASN1Object> 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<ASN1Object> 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<ASN1Object> 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);
  }

}
TOP

Related Classes of com.subgraph.orchid.crypto.RSAKeyEncoder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.