All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.palantir.conjure.java.config.ssl.pkcs1.Pkcs1PrivateKeyReader Maven / Gradle / Ivy

/*
 * (c) Copyright 2019 Palantir Technologies Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.palantir.conjure.java.config.ssl.pkcs1;

import com.palantir.logsafe.exceptions.SafeRuntimeException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.spec.RSAPrivateCrtKeySpec;
import java.security.spec.RSAPrivateKeySpec;

/**
 * Minimal DER parser that can handle ASN.1 (PKCS#1) private key's based on RFC 8017, appendix C.
 *
 * 

Handles private key in following format ignoring otherPrimeinfos, i.e. validates that version is two-prime(0). * *

 * RSAPrivateKey ::= SEQUENCE {
 *     version           Version,
 *     modulus           INTEGER,  -- n
 *     publicExponent    INTEGER,  -- e
 *     privateExponent   INTEGER,  -- d
 *     prime1            INTEGER,  -- p
 *     prime2            INTEGER,  -- q
 *     exponent1         INTEGER,  -- d mod (p-1)
 *     exponent2         INTEGER,  -- d mod (q-1)
 *     coefficient       INTEGER,  -- (inverse of q) mod p
 *     otherPrimeInfos   OtherPrimeInfos OPTIONAL
 * }
 *
 * Version ::= INTEGER { two-prime(0), multi(1) }
 *     (CONSTRAINED BY
 *       {-- version MUST
 *  be multi if otherPrimeInfos present --})
 *
 * OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
 *
 * OtherPrimeInfo ::= SEQUENCE {
 *     prime             INTEGER,  -- ri
 *     exponent          INTEGER,  -- di
 *     coefficient       INTEGER   -- ti
 * }
 * 
* *

see DER format specification for explanation behind length * parsing and tag values. ASN.1 uses CONSTRUCTED form of SEQUENCE tag hence the value is 48 instead of 16. */ public final class Pkcs1PrivateKeyReader { /** Tag value indicating an ASN.1 "INTEGER" value. */ private static final byte INTEGER = 0x02; /** Tag value indicating an ASN.1 "SEQUENCE" (zero to N elements, order is significant). */ private static final byte SEQUENCE = 0x30; private final ByteBuffer derBytes; public Pkcs1PrivateKeyReader(byte[] derBytes) { this.derBytes = ByteBuffer.wrap(derBytes); } public RSAPrivateKeySpec readRsaKey() { byte tag = derBytes.get(); if (tag != SEQUENCE) { throw new SafeRuntimeException("Expected SEQUENCE byte (0x30) at the beginning of RSA private key"); } int length = readLength(); // cap the read length derBytes.limit(derBytes.position() + length); BigInteger version = readNumber(); if (version.intValue() == 1) { throw new SafeRuntimeException("Only version 0 (two-prime) RSA keys are supported"); } BigInteger modulus = readNumber(); BigInteger publicExponent = readNumber(); BigInteger privateExponent = readNumber(); BigInteger primeP = readNumber(); BigInteger primeQ = readNumber(); BigInteger primeExponentP = readNumber(); BigInteger primeExponentQ = readNumber(); BigInteger crtCoefficient = readNumber(); if (publicExponent.signum() == 0 || primeExponentP.signum() == 0 || primeExponentQ.signum() == 0 || primeP.signum() == 0 || primeQ.signum() == 0 || crtCoefficient.signum() == 0) { return new RSAPrivateKeySpec(modulus, privateExponent); } else { return new RSAPrivateCrtKeySpec( modulus, publicExponent, privateExponent, primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient); } } private BigInteger readNumber() { byte tag = derBytes.get(); if (tag != INTEGER) { throw new SafeRuntimeException("Expected INTEGER byte (0x02) when reading a number"); } int length = readLength(); byte[] numberBytes = new byte[length]; System.arraycopy(derBytes.array(), derBytes.position(), numberBytes, 0, length); // move position in the buffer since arraycopy doesn't do it derBytes.position(derBytes.position() + length); return new BigInteger(numberBytes); } private int readLength() { byte lengthByte = derBytes.get(); if (lengthByte == -1) { throw new SafeRuntimeException("Unable to decode length from der bytes"); } if ((lengthByte & 0x080) == 0x00) { // short form, 1 byte length return lengthByte; } else if ((lengthByte & 0x07f) == 0x00) { // indefinite length encoding throw new SafeRuntimeException("Indefinite length encoding is not supported"); } else { // long form encoding int lengthOctets = lengthByte & 0x7f; // cap at 4 bytes for length value, 4GB of data. if (lengthOctets > 4) { throw new SafeRuntimeException("Values bigger than 4GBs are not supported"); } int length = 0; for (int i = 0; i < lengthOctets; i++) { length <<= 8; length += derBytes.get() & 0x0ff; } return length; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy