generateSelfSignedCertificate static method
Generates a self signed certificate
privateKey
= The private key used for signingcsr
= The CSR containing the DN and public keydays
= The validity in dayssans
= Subject alternative names to place within the certificatekeyUsage
= The key usage definition extensionextKeyUsage
= The extended key usage definitionserialNumber
= The serialnumber. If not set the default will be 1.issuer
= The issuer. If null, the issuer will be the subject of the given csr.notBefore
= The Timestamp after when the certificate is valid. If null, this will be DateTime.now. The "Not After" property of the certificate will have thedays
added tonotBefore
.
Implementation
static String generateSelfSignedCertificate(
PrivateKey privateKey,
String csr,
int days, {
List<String>? sans,
List<KeyUsage>? keyUsage,
List<ExtendedKeyUsage>? extKeyUsage,
String serialNumber = '1',
Map<String, String>? issuer,
DateTime? notBefore,
}) {
var csrData = csrFromPem(csr);
var data = ASN1Sequence();
// Add version
var version = ASN1Object(tag: 0xA0);
version.valueBytes = ASN1Integer.fromtInt(2).encode();
data.add(version);
// Add serial number
data.add(ASN1Integer(BigInt.parse(serialNumber)));
// Add protocol
var blockProtocol = ASN1Sequence();
blockProtocol.add(
ASN1ObjectIdentifier.fromIdentifierString(csrData.signatureAlgorithm));
blockProtocol.add(ASN1Null());
data.add(blockProtocol);
issuer ??= csrData.certificationRequestInfo!.subject!;
// Add Issuer
var issuerSeq = ASN1Sequence();
for (var k in issuer.keys) {
var value = issuer[k];
var pString;
if (StringUtils.isAscii(value!)) {
pString = ASN1PrintableString(stringValue: value);
} else {
pString = ASN1UTF8String(utf8StringValue: value);
}
var oIdentifier;
try {
oIdentifier = ASN1ObjectIdentifier.fromName(k);
} on UnsupportedObjectIdentifierException catch (e) {
oIdentifier = ASN1ObjectIdentifier.fromIdentifierString(k);
}
var innerSequence = ASN1Sequence(elements: [oIdentifier, pString]);
var s = ASN1Set(elements: [innerSequence]);
issuerSeq.add(s);
}
data.add(issuerSeq);
// Add Validity
var validitySeq = ASN1Sequence();
final DateTime from = notBefore ?? DateTime.now();
validitySeq.add(ASN1UtcTime(from));
validitySeq.add(ASN1UtcTime(from.add(Duration(days: days))));
data.add(validitySeq);
// Add Subject
var subjectSeq = ASN1Sequence();
for (var k in csrData.certificationRequestInfo!.subject!.keys) {
var value = csrData.certificationRequestInfo!.subject![k];
var pString;
if (StringUtils.isAscii(value!)) {
pString = ASN1PrintableString(stringValue: value);
} else {
pString = ASN1UTF8String(utf8StringValue: value);
}
var oIdentifier = ASN1ObjectIdentifier.fromIdentifierString(k);
var innerSequence = ASN1Sequence(elements: [oIdentifier, pString]);
var s = ASN1Set(elements: [innerSequence]);
subjectSeq.add(s);
}
data.add(subjectSeq);
// Add Public Key
if (privateKey.runtimeType == RSAPrivateKey) {
data.add(
_makePublicKeyBlock(
CryptoUtils.rsaPublicKeyFromDERBytes(
_stringAsBytes(
csrData.certificationRequestInfo!.publicKeyInfo!.bytes!),
),
),
);
} else {
data.add(
_makeEccPublicKeyBlock(
CryptoUtils.ecPublicKeyFromDerBytes(
_stringAsBytes(
csrData.certificationRequestInfo!.publicKeyInfo!.bytes!),
),
),
);
}
// Add Extensions
if (IterableUtils.isNotNullOrEmpty(sans) ||
IterableUtils.isNotNullOrEmpty(keyUsage) ||
IterableUtils.isNotNullOrEmpty(extKeyUsage)) {
var extensionTopSequence = ASN1Sequence();
if (IterableUtils.isNotNullOrEmpty(keyUsage)) {
int valueBytes = 1; // the last bit of the 2 bytes is always set
for (KeyUsage keyUsage in keyUsage!) {
final int shiftedBit = int.parse("8000", radix: 16) >> keyUsage.index;
valueBytes |=
shiftedBit; // bit shift from the first bit of the 2 bytes depending on which flag is set
}
final int firstValueByte =
(valueBytes & int.parse("ff00", radix: 16)) >> 8;
final int secondValueByte = (valueBytes & int.parse("00ff", radix: 16));
final Uint8List keyUsageBytes = Uint8List.fromList(<int>[
// BitString identifier
3,
// Length
3,
// Unused bytes at the end
1,
firstValueByte,
secondValueByte
]);
var octetString = ASN1OctetString(
octets: ASN1BitString.fromBytes(keyUsageBytes).encode());
var keyUsageSequence = ASN1Sequence();
keyUsageSequence
.add(ASN1ObjectIdentifier.fromIdentifierString('2.5.29.15'));
keyUsageSequence.add(octetString);
extensionTopSequence.add(keyUsageSequence);
}
if (IterableUtils.isNotNullOrEmpty(extKeyUsage)) {
var extKeyUsageList = ASN1Sequence();
for (var s in extKeyUsage!) {
var oi = <int>[];
switch (s) {
case ExtendedKeyUsage.SERVER_AUTH:
oi = [1, 3, 6, 1, 5, 5, 7, 3, 1];
break;
case ExtendedKeyUsage.CLIENT_AUTH:
oi = [1, 3, 6, 1, 5, 5, 7, 3, 2];
break;
case ExtendedKeyUsage.CODE_SIGNING:
oi = [1, 3, 6, 1, 5, 5, 7, 3, 3];
break;
case ExtendedKeyUsage.EMAIL_PROTECTION:
oi = [1, 3, 6, 1, 5, 5, 7, 3, 4];
break;
case ExtendedKeyUsage.TIME_STAMPING:
oi = [1, 3, 6, 1, 5, 5, 7, 3, 8];
break;
case ExtendedKeyUsage.OCSP_SIGNING:
oi = [1, 3, 6, 1, 5, 5, 7, 3, 9];
break;
case ExtendedKeyUsage.BIMI:
oi = [1, 3, 6, 1, 5, 5, 7, 3, 31];
break;
}
extKeyUsageList.add(ASN1ObjectIdentifier(oi));
}
var octetString = ASN1OctetString(octets: extKeyUsageList.encode());
var extKeyUsageSequence = ASN1Sequence();
extKeyUsageSequence
.add(ASN1ObjectIdentifier.fromIdentifierString('2.5.29.37'));
extKeyUsageSequence.add(octetString);
extensionTopSequence.add(extKeyUsageSequence);
}
if (IterableUtils.isNotNullOrEmpty(sans)) {
var sanList = ASN1Sequence();
for (var s in sans!) {
sanList.add(ASN1PrintableString(stringValue: s, tag: 0x82));
}
var octetString = ASN1OctetString(octets: sanList.encode());
var sanSequence = ASN1Sequence();
sanSequence.add(ASN1ObjectIdentifier.fromIdentifierString('2.5.29.17'));
sanSequence.add(octetString);
extensionTopSequence.add(sanSequence);
}
var extObj = ASN1Object(tag: 0xA3);
extObj.valueBytes = extensionTopSequence.encode();
data.add(extObj);
}
var outer = ASN1Sequence();
outer.add(data);
outer.add(blockProtocol);
if (privateKey.runtimeType == RSAPrivateKey) {
outer.add(ASN1BitString(
stringValues: _rsaSign(data.encode(), privateKey as RSAPrivateKey,
_getDigestFromOi(csrData.signatureAlgorithm!))));
} else {
var ecSignature = eccSign(data.encode(), privateKey as ECPrivateKey,
_getDigestFromOi(csrData.signatureAlgorithm!));
var bitStringSequence = ASN1Sequence();
bitStringSequence.add(ASN1Integer(ecSignature.r));
bitStringSequence.add(ASN1Integer(ecSignature.s));
var blockSignatureValue =
ASN1BitString(stringValues: bitStringSequence.encode());
outer.add(blockSignatureValue);
}
var chunks = StringUtils.chunk(base64.encode(outer.encode()), 64);
return '$BEGIN_CERT\n${chunks.join('\r\n')}\n$END_CERT';
}